diff --git a/slaeforms/app.py b/slaeforms/app.py index 034256e..0329efb 100644 --- a/slaeforms/app.py +++ b/slaeforms/app.py @@ -11,7 +11,7 @@ import uuid from sqlalchemy.exc import SQLAlchemyError from sqlalchemy import select, join from sqlalchemy.orm import DeclarativeBase -from flask_wtf.csrf import CSRFProtect +#from flask_wtf.csrf import CSRFProtect import os import csv from zipfile import ZipFile @@ -33,6 +33,7 @@ app = Flask(__name__) # configure the database, give it a path (it will be in the instances folder) app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db" app.config["PERMANENT_SESSION_LIFETIME"] = 10800 #3 Stunden, 10800 sekunden +app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000 # try and fix video upload not working db.init_app(app) #set the secret key (TODO change this for final deployment) @@ -41,6 +42,9 @@ app.secret_key = b"29fe9e8edd407c5491d4f1c05632d9fa33e26ed8734a3f5e080ebac3772a5 UPLOAD_FOLDER = 'uploads' EXPORT_FOLDER = 'exports' PASSWORD = '#1ACGmsjd' +# CONFIGFILE = 'userstudy1.json' +CONFIGFILE = 'test.json' +# CONFIGFILE = 'default.json' #csrf = CSRFProtect(app) #enable CSRF protection globally @@ -70,7 +74,7 @@ except SQLAlchemyError as e: #open, parse and execute json file #open the json file with the config -configfile = open("default.json", encoding='utf-8') #todo replace with other name +configfile = open(CONFIGFILE, encoding='utf-8') #todo replace with other name #convert it to dict config = json.load(configfile) configfile.close() @@ -374,16 +378,6 @@ def sendpage(): print("Error occurred: {e}".format(e=str(e))) return "There was a problem while adding the response to the Database" - # handle possible Video that was send - if 'recordedVideo' in request.files: - video = request.files['recordedVideo'] - formatted_date = date.strftime("%Y.%m.%d %H-%M-%S") - print("date: ", date) - video_name = str(session_user_id) + "_" + session["current_block_name"] + "_" + session["current_stimulus_name"] + "_" + str(formatted_date) + ".webm" - path = os.path.join(UPLOAD_FOLDER, video_name) - print("path: ",path) - os.makedirs(UPLOAD_FOLDER, exist_ok=True) - video.save(path) if (session["current_block_index"] == session["number_of_blocks"]-1) and (session["current_stimulus_index"] >= session["number_of_stimuli"]-1): #update the database entry, the form is completed diff --git a/slaeforms/static/videoscript.js b/slaeforms/static/videoscript.js index 25dd1d0..f0ef365 100644 --- a/slaeforms/static/videoscript.js +++ b/slaeforms/static/videoscript.js @@ -1,64 +1,73 @@ const buttonCamera = document.getElementById('buttonCamera'); const buttonRecord = document.getElementById('buttonRecord'); const buttonDelete = document.getElementById('buttonDelete'); -const videoDisplay = document.getElementById('videoDisplay'); const buttonCameraIcon = document.getElementById('buttonCameraIcon'); const buttonRecordIcon = document.getElementById('buttonRecordIcon'); const buttonDeleteIcon = document.getElementById('buttonDeleteIcon'); -const videoContainer = document.getElementById('videoContainer'); -const videoContainerCss = document.querySelector(".video-container ") //might be unnecessary -var mediaRecorder = null; -var stream = null; -let recordedVideoBlob = null; -let isRecording = false; -let videoAccess = false; -let videoHeigt = 720; + +const videoDisplay = document.getElementById('videoDisplay'); +const videoContainer = document.getElementById('videoContainer'); // div that contains the videodisplay + +var mediaRecorder = null; // interface of the Media Stream Recording API to record the video stream +var stream = null; // webcam video input stream +let recordedVideoBlob = null; // recorded videodata +let isRecording = false; // false to display record button, true to display stop button +let videoAccess = false; // true if user has given permission to use the webcam + +// default video dimensions +let videoHeight = 720; let videoWidth = 1280; -// Handle form submission +// handle form submission +//the Video is beeing send together with the form data document.getElementById("question_form").addEventListener("submit", function (event) { event.preventDefault(); // Prevent the default form submission console.log("submit button pressed"); - // Create a FormData object + // create a FormData object const formData = new FormData(event.target); console.log("form data: ",formData); - // Append the recorded video Blob to the FormData object + // append the recorded video blob to the FormData object if (recordedVideoBlob) { console.log("video is available: ", recordedVideoBlob); formData.append("recordedVideo", recordedVideoBlob, "recordedVideo.webm"); } - console.log("Data to be submitted: ",formData); - // Use fetch to send the form data and video to the backend console.log("try to send the form and video"); + + // send the form data and video to the backend fetch("/send", { method: "POST", body: formData }).then(response => { + // code to handle proper redirection after submit console.log('Response:', response); - // Check if response is a redirect (HTTP 3xx status) + // check if response is a redirect + /* if (response.redirected) { console.log("Redirecting to:", response.url); window.location.href = response.url; // Redirect to the new page } else { - console.log("Non-redirect response received."); - // Handle other responses if needed + console.log("No redirect response received."); } + */ }) .catch(error => { console.error('Error:', error); - // Handle errors if fetch fails }); }); +// enable/disable video recording feature async function cameraButton() { if (!videoAccess) { console.log("cameraButton case videoAccess = false"); + try { + // ask the user for permission to use the webcam + // if given, assign it to "stream" stream = await navigator.mediaDevices.getUserMedia({ video: true, }); @@ -67,39 +76,61 @@ async function cameraButton() { return; } console.log("stream is active"); + videoAccess = true; - videoHeigt = stream.getVideoTracks()[0].getSettings().height + // set the dimensions of the webcam stream to the video display + videoHeight = stream.getVideoTracks()[0].getSettings().height videoWidth = stream.getVideoTracks()[0].getSettings().width - let aspectratio = (videoHeigt / videoWidth) *100 - console.log("videoHeigt: ",videoHeigt); - console.log("videoWidth: ",videoWidth); - console.log("aspect ratio: ",aspectratio); - console.log("device: ",stream.getVideoTracks()[0].getSettings().deviceId); - videoContainer.style.setProperty("padding-bottom", "min("+videoHeigt+"px,"+aspectratio+"%)"); + // calculate the aspec ratio + let aspectratio = (videoHeight / videoWidth) *100; + //console.log("videoHeight: ",videoHeight); + //console.log("videoWidth: ",videoWidth); + //console.log("aspect ratio: ",aspectratio); + //console.log("device: ",stream.getVideoTracks()[0].getSettings().deviceId); - if (videoHeigt > videoWidth){ //hochkant video - videoContainer.style.setProperty("max-width", "min(80%,576"); - }else{ //Normal, horizontal + // adjust CSS to make the video fit properly + // this makes sure that there is no buggy "jumping" when the video display source changes + videoContainer.style.setProperty("padding-bottom", "min("+videoHeight+"px,"+aspectratio+"%), 100vh"); + + + //TODO: Solution for this part: + // If the video is vertical, I need to set the width to 0, the max-height to 100% and do the padding on the width, so turn the code around + //This should always make the video fitt + + // adjust the max width for horizontal and vertical video input + if (videoHeight > videoWidth){ //hochkant video + // I did this originally to display vertical video on a horizontal screen properly (video should not be higher than the screem) + // but now I do this by adjusting the paddint to be at most 100vh, so this whole part should be useless now + // But a new problem could be if I do too little padding and the ratio is lost because of it, so actually I also need to adjust the width + videoContainer.style.setProperty("max-width", "min(80%,576)"); // maybe actually use the screen height here: instead of 80% maybe 80vh + }else{ //normal, horizontal videoContainer.style.setProperty("max-width", "100%"); } + // show the webcam input stream on the video display videoDisplay.srcObject = stream; + // change the icon to indicate the disable option buttonCameraIcon.src = ICON_PATHS.cameraofficon; buttonCameraIcon.alt = "Camera-off Icon"; + + // display the elements for the video recording buttonRecord.style.display = 'inline-block'; buttonDelete.style.display = 'inline-block'; videoDisplay.style.display = 'block'; videoContainer.style.display = 'block'; videoDisplay.classList.add("videomirror"); + // initialize MediaRecorder, give it the webcam stream as input to record mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm", // could use different video format // videoBitsPerSecond: 5000000, // Standard bitrate for video is 2,5 mbps }); + // when data is available at the mediaRecorder (this means the recording has ended, since I have not specified time intervalls) + // save the data in "recordedVideoBlob" mediaRecorder.addEventListener("dataavailable", (event) => { console.log("Data available Event triggered"); if (event.data.size > 0) { @@ -110,28 +141,37 @@ async function cameraButton() { } }); - mediaRecorder.addEventListener("stop", () => { + // when recording is stopped, display the recorded video + // TODO possibly replace this and just add it to the dataavailable event + // TODO this would ensure proper order in a concurrent scenario (the stop code here can only be executed after the dataavailable code happened) + // TODO part of it could also go to the record button function + mediaRecorder.addEventListener("stop", () => { if (recordedVideoBlob) { - videoDisplay.srcObject = null; - videoDisplay.src = URL.createObjectURL(recordedVideoBlob); - videoDisplay.controls = true; - videoDisplay.pause(); + videoDisplay.srcObject = null; //remove the stream as input + videoDisplay.src = URL.createObjectURL(recordedVideoBlob); // create a url for the recorded video and set it as input + videoDisplay.controls = true; // display video controls + videoDisplay.pause(); // pause, so that it doesn't immediately play, TODO maybe disable autoplay? - buttonRecordIcon.src = ICON_PATHS.recordicon; + // TODO maybe move this to the record button function + buttonRecordIcon.src = ICON_PATHS.recordicon; //change the icon of the record button back buttonRecordIcon.alt = 'Record Icon'; isRecording = false; + console.log('Recording stopped'); console.log("Src path:", videoDisplay.src); } }); } else { + // -> the user disabled the videorecording feature + console.log("cameraButton case videoAccess = true"); - stream.getTracks().forEach(track => track.stop()); // Stop all tracks to release the camera + stream.getTracks().forEach(track => track.stop()); // stop all tracks to release the camera stream = null; videoAccess = false; + // change the icon and hide all the elements again buttonCameraIcon.src = ICON_PATHS.cameraicon; buttonCameraIcon.alt = "Camera Icon"; buttonRecord.style.display = 'none'; @@ -142,41 +182,57 @@ async function cameraButton() { console.log("camera button function ends"); } + function recordButton() { console.log("recordButton pressed"); if (!isRecording) { + // a new recording starts + console.log("recordButton pressed case isRecording = false"); - deleteButton(); - videoDisplay.classList.add("videomirror"); - buttonDelete.setAttribute("disabled", ""); - buttonDeleteIcon.classList.add("buttondisable"); - videoDisplay.srcObject = stream; - videoDisplay.src = null; - videoDisplay.controls = false; - mediaRecorder.start(); + + deleteButton(); //delete previous video + + videoDisplay.classList.add("videomirror"); // mirror the video while recording, for a more natural experience + buttonDelete.setAttribute("disabled", ""); // can't delete during recording + buttonDeleteIcon.classList.add("buttondisable"); // delete button icon indicates that it is disabled + + videoDisplay.src = null; // get rid of the previous video on the display + videoDisplay.srcObject = stream; // set webcam stream as input for the video display + videoDisplay.controls = false; // hide the video controls durin recording, because they are confusing + mediaRecorder.start(); // start recording + console.log("video bitrate = ",mediaRecorder.videoBitsPerSecond); + // change icon to indicate "stop" buttonRecordIcon.src = ICON_PATHS.stopicon; buttonRecordIcon.alt = 'Stop Icon'; isRecording = true; + console.log('Recording started'); } else { + // recording got stopped + console.log("recordButton pressed case isRecording = true"); - mediaRecorder.stop(); - videoDisplay.classList.remove("videomirror"); + + mediaRecorder.stop(); // stop recording + videoDisplay.classList.remove("videomirror"); // don't mirror the videodisplay anymore console.log("recording stops"); + + // enable the deletebutton again buttonDelete.removeAttribute("disabled"); buttonDeleteIcon.classList.remove("buttondisable"); } } function deleteButton() { - // TODO delete data + // delete the recorded video videoDisplay.controls = false; - videoDisplay.srcObject = stream; + videoDisplay.srcObject = stream; // set webcam stream as source for the video display again recordedVideoBlob = null - videoDisplay.classList.add("videomirror"); + videoDisplay.classList.add("videomirror"); // mirror the video display during live feed again + + // disable the deletebutton again buttonDelete.setAttribute("disabled", ""); buttonDeleteIcon.classList.add("buttondisable"); } diff --git a/slaeforms/templates/standard_template.html b/slaeforms/templates/standard_template.html index 967a75a..db061b8 100644 --- a/slaeforms/templates/standard_template.html +++ b/slaeforms/templates/standard_template.html @@ -225,7 +225,6 @@ step={{question["step"]}} {% else %}
Error: Block {{config["question 1"]["blocks"][block]["type"]}} could not be loaded!
diff --git a/slaeforms/test.json b/slaeforms/test.json index 6981cf1..20aae20 100644 --- a/slaeforms/test.json +++ b/slaeforms/test.json @@ -1,37 +1,325 @@ { - "Block 1":{ - "type": "SinglePage", - "template": "startpage.html" - }, - "Block 2":{ + + "Block 2": { "type": "TaskTemplate", - "tempalte": "tempaltetest1.html", - "name" : "Block2Responses", - "databasetable": { - "question_title" : { - "type":"likert" + "tempalte": "standard_template.html", + "stimuli": { + "type": "single_video", + "order": "random", + "list": { + "video_1": "https://www.youtube-nocookie.com/embed/iI2bjpgLgu0?si=pWWwitq8Tl9zAs04", + "video_2": "https://www.youtube-nocookie.com/embed/R_LNEJiShRM?si=vm5mj-xs04nfijkf", + "video_3": "https://www.youtube-nocookie.com/embed/DJnHVnT2YEk?si=e6qe7nrHaRYJ3NQe", + "video_4": "https://www.youtube-nocookie.com/embed/6BTZn7tqSzk?si=jQH8RHGMIP9ARsT5", + "video_5": "https://www.youtube-nocookie.com/embed/bCRqmhWjuCk?si=iM2dwOSTosLq08Pr", + "video_6": "https://www.youtube-nocookie.com/embed/hgE2k8y9-qc?si=kR2rshmVcb9P70r0" + }, + "configuration": { + "embed": "yt" + } + }, + "questions": { + "question1": { + "type": "likert", + "name": "likertscale", + "text": "Wie wĂŒrden sie dieses Video bewerten?", + "required": "true", + "points": { + "p1": { + "value": "1", + "text": "Ich finde es gar nicht gut đđ" + }, + "p2": { + "value": "2", + "text": "Ich finde es nicht gut đ" + }, + "p3": { + "value": "3", + "text": "Ich finde es weder gut noch schlecht" + }, + "p4": { + "value": "4", + "text": "Ich finde es gut đ" + }, + "p5": { + "value": "5", + "text": "Ich finde es sehr gut đđ" + } + } + }, + "question2": { + "type": "textinput", + "name": "text_feedback", + "text": "Hier können sie uns Feedback geben", + "required": "false", + "size": "250" + }, + "question3": { + "type": "videoinput", + "text": "Hier können sie per Video Feedback geben", + "name": "video_feedback", + "required": "false" + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden.\\n Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt.\\n Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "single_video_responses", + "fields": { + "likertscale": { + "type": "integer", + "nullable": "false" + }, + "text_feedback": { + "type": "string", + "size": "250", + "nullable": "true" + }, + "video_upload": { + "type": "string", + "size": "100", + "nullable": "true" + } } } }, - "question 3":{ - "type": "single", - "video1": "https://www.youtube-nocookie.com/embed/XTMIomsXxKM?si=r2zB6OKERH6Jdpi6", - "scales": { - "block1":{ - "type": "likert", - "numberofpoints": "3", - "points":{ - "point1": "left", - "point2": "none", - "point3": "right" + "Block 1": { + "type": "TaskTemplate", + "tempalte": "standard_template.html", + "stimuli": { + "type": "empty", + "list": { + "empty_stimulus": "" + } + }, + "questions": { + "question1_alter": { + "type": "numberinput", + "name": "alter", + "text": "Alter:", + "required": "true", + "min": "1", + "max": "120" + }, + "question2_geschlecht": { + "type": "dropdowninput", + "name": "geschlecht", + "text": "Geschlecht:", + "required": "true", + "defaulttext": "", + "points": { + "mĂ€nnlich": { + "value": "MĂ€nnlich", + "text": "MĂ€nnlich" + }, + "weiblich": { + "value": "Weiblich", + "text": "Weiblich" + }, + "divers": { + "value": "Divers", + "text": "Divers" + }, + "keine_angabe": { + "value": "keine_angabe", + "text": "Keine Angabe" + } } }, - "block2":{ - "type": "textinput", - "length": "200" + "question3_hoerstatus": { + "type": "dropdowninput", + "name": "hoerstatus", + "text": "Hörstatus:", + "required": "true", + "defaulttext": "", + "points": { + "hörend": { + "value": "Hörend", + "text": "Hörend" + }, + "schwerhörig": { + "value": "Schwerhörig", + "text": "Schwerhörig" + }, + "gehörlos": { + "value": "Gehörlos", + "text": "Gehörlos" + } + } }, - "block3":{ - "type": "video" + "question4_bevorzugte_kommunikation": { + "type": "dropdowninput", + "name": "bevorzugte_kommunikation", + "text": "Bevorzugte Kommunikationsform:", + "required": "true", + "defaulttext": "", + "points": { + "gesprochen": { + "value": "Gesprochene Sprache", + "text": "Gesprochene Sprache" + }, + "text": { + "value": "Text", + "text": "Text" + }, + "gebĂ€rdensprache": { + "value": "GebĂ€rdensprache", + "text": "GebĂ€rdensprache" + } + } + }, + "question5_gebeardenzeitraum": { + "type": "numberinput", + "name": "gebĂ€rdenzeitraum", + "text": "Wie viele Jahre verwenden sie schon GebĂ€rdensprache:", + "required": "true", + "min": "0", + "max": "100", + "step": "0.5" + }, + "question6_sprachkompetenz": { + "type": "numberinput", + "name": "gebĂ€rdensprachkompetenz", + "text": "Wie schĂ€tzen sie ihre GebĂ€rdensprachkompetenz ein (1-10):", + "required": "true", + "min": "1", + "max": "10" + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden. \n Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt. Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "demographic_data", + "fields": { + "alter": { + "type": "integer", + "nullable": "false" + }, + "geschlecht": { + "type": "string", + "size": "14", + "nullable": "false" + }, + "hoerstatus": { + "type": "string", + "size": "14", + "nullable": "false" + }, + "bevorzugte_kommunikation": { + "type": "string", + "size": "22", + "nullable": "false" + }, + "gebĂ€rdenzeitraum": { + "type": "float", + "nullable": "false" + }, + "gebĂ€rdensprachkompetenz": { + "type": "integer", + "nullable": "false" + } + } + } + }, + "Block 3": { + "type": "TaskTemplate", + "tempalte": "standard_template.html", + "stimuli": { + "type": "double_video", + "list_1": { + "video_1": "https://www.youtube-nocookie.com/embed/IqGVT1q1PtM?si=kel7ZWEQl3h-h522", + "video_2": "https://www.youtube-nocookie.com/embed/g9KA72jN5SM?si=O7dfqTXdFCCAScJ-" + }, + "list_2": { + "video_2": "https://www.youtube-nocookie.com/embed/g9KA72jN5SM?si=O7dfqTXdFCCAScJ-", + "video_1": "https://www.youtube-nocookie.com/embed/IqGVT1q1PtM?si=kel7ZWEQl3h-h522" + }, + "configuration": { + "embed": "yt" + } + }, + "questions": { + "question1": { + "type": "likert", + "name": "likertscale", + "text": "Welches Video gefĂ€llt ihnen besser?", + "required": "true", + "points": { + "p1": { + "value": "1", + "text": "Ich finde das linke Video besser" + }, + "p2": { + "value": "2", + "text": "Ich finde beide Videos gleich gut" + }, + "p3": { + "value": "3", + "text": "Ich finde das rechte Video besser" + } + } + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden. Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt. Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "double_video_responses", + "fields": { + "likertscale": { + "type": "integer", + "nullable": "false" + } + } + } + }, + "Block 4": { + "type": "TaskTemplate", + "tempalte": "standard_template.html", + "stimuli": { + "type": "empty", + "list": { + "empty_stimulus": "" + } + }, + "questions": { + "question1": { + "type": "textinput", + "name": "formfeedback", + "text": "Das war der Prototyp fĂŒr dieses Studientool. Ăber Feedback wĂŒrde ich mich freuen. Entweder hier oder per Email unter: jan.dickmann@web.de", + "required": "false", + "size": "1000" + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden. Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt. Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "feedback_responses", + "fields": { + "formfeedback": { + "type": "string", + "size": "1000", + "nullable": "true" + } } } } diff --git a/slaeforms/userstudy1.json b/slaeforms/userstudy1.json new file mode 100644 index 0000000..2687778 --- /dev/null +++ b/slaeforms/userstudy1.json @@ -0,0 +1,325 @@ +{ + "Block 1": { + "type": "TaskTemplate", + "tempalte": "standard_template.html", + "stimuli": { + "type": "empty", + "list": { + "empty_stimulus": "" + } + }, + "questions": { + "question1_alter": { + "type": "numberinput", + "name": "alter", + "text": "Alter:", + "required": "true", + "min": "1", + "max": "120" + }, + "question2_geschlecht": { + "type": "dropdowninput", + "name": "geschlecht", + "text": "Geschlecht:", + "required": "true", + "defaulttext": "", + "points": { + "mĂ€nnlich": { + "value": "MĂ€nnlich", + "text": "MĂ€nnlich" + }, + "weiblich": { + "value": "Weiblich", + "text": "Weiblich" + }, + "divers": { + "value": "Divers", + "text": "Divers" + }, + "keine_angabe": { + "value": "keine_angabe", + "text": "Keine Angabe" + } + } + }, + "question3_hoerstatus": { + "type": "dropdowninput", + "name": "hoerstatus", + "text": "Hörstatus:", + "required": "true", + "defaulttext": "", + "points": { + "hörend": { + "value": "Hörend", + "text": "Hörend" + }, + "schwerhörig": { + "value": "Schwerhörig", + "text": "Schwerhörig" + }, + "gehörlos": { + "value": "Gehörlos", + "text": "Gehörlos" + } + } + }, + "question4_bevorzugte_kommunikation": { + "type": "dropdowninput", + "name": "bevorzugte_kommunikation", + "text": "Bevorzugte Kommunikationsform:", + "required": "true", + "defaulttext": "", + "points": { + "gesprochen": { + "value": "Gesprochene Sprache", + "text": "Gesprochene Sprache" + }, + "text": { + "value": "Text", + "text": "Text" + }, + "gebĂ€rdensprache": { + "value": "GebĂ€rdensprache", + "text": "GebĂ€rdensprache" + } + } + }, + "question5_gebeardenzeitraum": { + "type": "numberinput", + "name": "gebĂ€rdenzeitraum", + "text": "Wie viele Jahre verwenden sie schon GebĂ€rdensprache:", + "required": "true", + "min": "0", + "max": "100", + "step": "0.5" + }, + "question6_sprachkompetenz": { + "type": "numberinput", + "name": "gebĂ€rdensprachkompetenz", + "text": "Wie schĂ€tzen sie ihre GebĂ€rdensprachkompetenz ein (1-10):", + "required": "true", + "min": "1", + "max": "10" + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden. \n Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt. Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "demographic_data", + "fields": { + "alter": { + "type": "integer", + "nullable": "false" + }, + "geschlecht": { + "type": "string", + "size": "14", + "nullable": "false" + }, + "hoerstatus": { + "type": "string", + "size": "14", + "nullable": "false" + }, + "bevorzugte_kommunikation": { + "type": "string", + "size": "22", + "nullable": "false" + }, + "gebĂ€rdenzeitraum": { + "type": "float", + "nullable": "false" + }, + "gebĂ€rdensprachkompetenz": { + "type": "integer", + "nullable": "false" + } + } + } + }, + "Block 2": { + "type": "TaskTemplate", + "tempalte": "standard_template.html", + "stimuli": { + "type": "single_video", + "order": "random", + "list": { + "video_1": "https://www.youtube-nocookie.com/embed/iI2bjpgLgu0?si=pWWwitq8Tl9zAs04", + "video_2": "https://www.youtube-nocookie.com/embed/R_LNEJiShRM?si=vm5mj-xs04nfijkf", + "video_3": "https://www.youtube-nocookie.com/embed/DJnHVnT2YEk?si=e6qe7nrHaRYJ3NQe", + "video_4": "https://www.youtube-nocookie.com/embed/6BTZn7tqSzk?si=jQH8RHGMIP9ARsT5", + "video_5": "https://www.youtube-nocookie.com/embed/bCRqmhWjuCk?si=iM2dwOSTosLq08Pr", + "video_6": "https://www.youtube-nocookie.com/embed/hgE2k8y9-qc?si=kR2rshmVcb9P70r0" + }, + "configuration": { + "embed": "yt" + } + }, + "questions": { + "question1": { + "type": "likert", + "name": "likertscale", + "text": "Wie wĂŒrden sie dieses Video bewerten?", + "required": "true", + "points": { + "p1": { + "value": "1", + "text": "Ich finde es gar nicht gut đđ" + }, + "p2": { + "value": "2", + "text": "Ich finde es nicht gut đ" + }, + "p3": { + "value": "3", + "text": "Ich finde es weder gut noch schlecht" + }, + "p4": { + "value": "4", + "text": "Ich finde es gut đ" + }, + "p5": { + "value": "5", + "text": "Ich finde es sehr gut đđ" + } + } + }, + "question2": { + "type": "textinput", + "name": "text_feedback", + "text": "Hier können sie uns Feedback geben", + "required": "false", + "size": "250" + }, + "question3": { + "type": "videoinput", + "text": "Hier können sie per Video Feedback geben", + "name": "video_feedback", + "required": "false" + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden.\\n Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt.\\n Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "single_video_responses", + "fields": { + "likertscale": { + "type": "integer", + "nullable": "false" + }, + "text_feedback": { + "type": "string", + "size": "250", + "nullable": "true" + }, + "video_upload": { + "type": "string", + "size": "100", + "nullable": "true" + } + } + } + }, + "Block 3": { + "type": "TaskTemplate", + "tempalte": "standard_template.html", + "stimuli": { + "type": "double_video", + "list_1": { + "video_1": "https://www.youtube-nocookie.com/embed/IqGVT1q1PtM?si=kel7ZWEQl3h-h522", + "video_2": "https://www.youtube-nocookie.com/embed/g9KA72jN5SM?si=O7dfqTXdFCCAScJ-" + }, + "list_2": { + "video_2": "https://www.youtube-nocookie.com/embed/g9KA72jN5SM?si=O7dfqTXdFCCAScJ-", + "video_1": "https://www.youtube-nocookie.com/embed/IqGVT1q1PtM?si=kel7ZWEQl3h-h522" + }, + "configuration": { + "embed": "yt" + } + }, + "questions": { + "question1": { + "type": "likert", + "name": "likertscale", + "text": "Welches Video gefĂ€llt ihnen besser?", + "required": "true", + "points": { + "p1": { + "value": "1", + "text": "Ich finde das linke Video besser" + }, + "p2": { + "value": "2", + "text": "Ich finde beide Videos gleich gut" + }, + "p3": { + "value": "3", + "text": "Ich finde das rechte Video besser" + } + } + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden. Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt. Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "double_video_responses", + "fields": { + "likertscale": { + "type": "integer", + "nullable": "false" + } + } + } + }, + "Block 4": { + "type": "TaskTemplate", + "tempalte": "standard_template.html", + "stimuli": { + "type": "empty", + "list": { + "empty_stimulus": "" + } + }, + "questions": { + "question1": { + "type": "textinput", + "name": "formfeedback", + "text": "Das war der Prototyp fĂŒr dieses Studientool. Ăber Feedback wĂŒrde ich mich freuen. Entweder hier oder per Email unter: jan.dickmann@web.de", + "required": "false", + "size": "1000" + } + }, + "infovideo": { + "videourl": "https://www.youtube-nocookie.com/embed/F_w50c5Us3Y?si=-H07MmQ4lYOC2Bwh", + "infotext": "Hier wird in Zukunft ein ErklĂ€rtext stehen, in dem die Fragestellungen erklĂ€rt werden. Dazu werden alle Fragen der Seite einzeln durchgegangen und einfach erklĂ€rt. Zum Beispiel wird hier erklĂ€rt, dass man um Feedback zu geben, ein Video aufnehmen kann. Dazu drĂŒckt man auf den Knopf mit dem Videokamera Symbol. Danach muss man oben links am Browser bestĂ€tigen, dass der Browser auf die Kamera zugreifen darf.", + "configuration": { + "embed": "yt" + } + }, + "database_table": { + "table_name": "feedback_responses", + "fields": { + "formfeedback": { + "type": "string", + "size": "1000", + "nullable": "true" + } + } + } + } +} \ No newline at end of file diff --git a/slaeforms/zip_exports/all_videos.zip b/slaeforms/zip_exports/all_videos.zip index 15cb0ec..e8db491 100644 Binary files a/slaeforms/zip_exports/all_videos.zip and b/slaeforms/zip_exports/all_videos.zip differ