From 63ffaf02a657b4f57602b63ddf6d9c8f393d4f64 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 18 Jul 2024 14:20:44 +0200 Subject: [PATCH 1/5] Added documentation to videoscript.js --- slaeforms/app.py | 2 +- slaeforms/static/videoscript.js | 133 +++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 44 deletions(-) diff --git a/slaeforms/app.py b/slaeforms/app.py index 034256e..0aca2aa 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 diff --git a/slaeforms/static/videoscript.js b/slaeforms/static/videoscript.js index 25dd1d0..9cb8681 100644 --- a/slaeforms/static/videoscript.js +++ b/slaeforms/static/videoscript.js @@ -1,64 +1,72 @@ 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 +const videoContainerCss = document.querySelector('.video-container') //might be unnecessary + +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 videoHeigt = 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 +75,53 @@ async function cameraButton() { return; } console.log("stream is active"); + videoAccess = true; + // set the dimensions of the webcam stream to the video display videoHeigt = stream.getVideoTracks()[0].getSettings().height videoWidth = stream.getVideoTracks()[0].getSettings().width + // calculate the aspec ratio 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); + //console.log("videoHeigt: ",videoHeigt); + //console.log("videoWidth: ",videoWidth); + //console.log("aspect ratio: ",aspectratio); + //console.log("device: ",stream.getVideoTracks()[0].getSettings().deviceId); + + // 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("+videoHeigt+"px,"+aspectratio+"%)"); + // adjust the max width for horizontal and vertical video input if (videoHeigt > videoWidth){ //hochkant video videoContainer.style.setProperty("max-width", "min(80%,576"); - }else{ //Normal, horizontal + }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 +132,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 +173,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"); } From dfb37e4a86c0b38c9b0928ec270226e2d619c2cc Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 7 Aug 2024 13:56:15 +0200 Subject: [PATCH 2/5] typo fixed --- slaeforms/static/videoscript.js | 13 ++++++------- slaeforms/templates/standard_template.html | 1 - 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/slaeforms/static/videoscript.js b/slaeforms/static/videoscript.js index 9cb8681..4497511 100644 --- a/slaeforms/static/videoscript.js +++ b/slaeforms/static/videoscript.js @@ -7,7 +7,6 @@ const buttonDeleteIcon = document.getElementById('buttonDeleteIcon'); const videoDisplay = document.getElementById('videoDisplay'); const videoContainer = document.getElementById('videoContainer'); // div that contains the videodisplay -const videoContainerCss = document.querySelector('.video-container') //might be unnecessary var mediaRecorder = null; // interface of the Media Stream Recording API to record the video stream var stream = null; // webcam video input stream @@ -16,7 +15,7 @@ let isRecording = false; // false to display record button, true to display s let videoAccess = false; // true if user has given permission to use the webcam // default video dimensions -let videoHeigt = 720; +let videoHeight = 720; let videoWidth = 1280; // handle form submission @@ -79,22 +78,22 @@ async function cameraButton() { videoAccess = true; // set the dimensions of the webcam stream to the video display - videoHeigt = stream.getVideoTracks()[0].getSettings().height + videoHeight = stream.getVideoTracks()[0].getSettings().height videoWidth = stream.getVideoTracks()[0].getSettings().width // calculate the aspec ratio - let aspectratio = (videoHeigt / videoWidth) *100 - //console.log("videoHeigt: ",videoHeigt); + 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); // 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("+videoHeigt+"px,"+aspectratio+"%)"); + videoContainer.style.setProperty("padding-bottom", "min("+videoHeight+"px,"+aspectratio+"%)"); // adjust the max width for horizontal and vertical video input - if (videoHeigt > videoWidth){ //hochkant video + if (videoHeight > videoWidth){ //hochkant video videoContainer.style.setProperty("max-width", "min(80%,576"); }else{ //normal, horizontal videoContainer.style.setProperty("max-width", "100%"); 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!

From b1e939adebe5672f84c9b0880b95df29703b6cba Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 20 Aug 2024 15:30:38 +0200 Subject: [PATCH 3/5] Trying to fix video upload (not working at all yet) --- slaeforms/app.py | 20 +- slaeforms/static/videoscript.js | 14 +- slaeforms/test.json | 340 +++++++++++++++++++++++++-- slaeforms/userstudy1.json | 325 +++++++++++++++++++++++++ slaeforms/zip_exports/all_videos.zip | Bin 22 -> 268 bytes 5 files changed, 670 insertions(+), 29 deletions(-) create mode 100644 slaeforms/userstudy1.json diff --git a/slaeforms/app.py b/slaeforms/app.py index 0aca2aa..b7acc42 100644 --- a/slaeforms/app.py +++ b/slaeforms/app.py @@ -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() @@ -346,7 +350,20 @@ def sendpage(): # handle possible Video that was send if 'recordedVideo' in request.files: + print("------------------------trying to save the video-------------------------------") + print("request.max_content_length:", request.max_content_length) + print("request.max_form_memory_size:", request.max_form_memory_size) + print("request.accept_mimetypes:", request.accept_mimetypes) + print("request.args:", request.args) + print("request.content_length:", request.content_length) video = request.files['recordedVideo'] + print("request.data:", request.data) + print("request.files:", request.files) + print("request.form:", request.form) + print("video: ",video) + print("video.content_length: ", video.content_length) + print("MIME type: ", video.mimetype) + print("File name: ", video.filename) 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" @@ -354,6 +371,7 @@ def sendpage(): print("path: ",path) os.makedirs(UPLOAD_FOLDER, exist_ok=True) video.save(path) + print("------------------------END trying to save the video-------------------------------") if 'recordedVideo' in request.files: if hasattr(new_entry, "video_upload"): diff --git a/slaeforms/static/videoscript.js b/slaeforms/static/videoscript.js index 4497511..f0ef365 100644 --- a/slaeforms/static/videoscript.js +++ b/slaeforms/static/videoscript.js @@ -46,12 +46,14 @@ document.getElementById("question_form").addEventListener("submit", function (ev // code to handle proper redirection after submit console.log('Response:', response); // 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("No redirect response received."); } + */ }) .catch(error => { console.error('Error:', error); @@ -90,11 +92,19 @@ async function cameraButton() { // 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+"%)"); + 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 - videoContainer.style.setProperty("max-width", "min(80%,576"); + // 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%"); } 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 15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7..e8db4917f519d1f5af9ddddb7fb0db0837dab34b 100644 GIT binary patch literal 268 zcmWIWW@Zs#00HGvkw`EDN`wOG(t@1)#FS$FBtrvp<218mT? Date: Tue, 20 Aug 2024 15:48:05 +0200 Subject: [PATCH 4/5] removing comments again, but still not working --- slaeforms/app.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/slaeforms/app.py b/slaeforms/app.py index b7acc42..7816fcd 100644 --- a/slaeforms/app.py +++ b/slaeforms/app.py @@ -351,19 +351,19 @@ def sendpage(): # handle possible Video that was send if 'recordedVideo' in request.files: print("------------------------trying to save the video-------------------------------") - print("request.max_content_length:", request.max_content_length) - print("request.max_form_memory_size:", request.max_form_memory_size) - print("request.accept_mimetypes:", request.accept_mimetypes) - print("request.args:", request.args) - print("request.content_length:", request.content_length) + #print("request.max_content_length:", request.max_content_length) + #print("request.max_form_memory_size:", request.max_form_memory_size) + #print("request.accept_mimetypes:", request.accept_mimetypes) + #print("request.args:", request.args) + #print("request.content_length:", request.content_length) video = request.files['recordedVideo'] - print("request.data:", request.data) - print("request.files:", request.files) - print("request.form:", request.form) + #print("request.data:", request.data) + #print("request.files:", request.files) + #print("request.form:", request.form) print("video: ",video) print("video.content_length: ", video.content_length) - print("MIME type: ", video.mimetype) - print("File name: ", video.filename) + #print("MIME type: ", video.mimetype) + #print("File name: ", video.filename) 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" From 3eac9d64cb4ad5f61a1142620e26832f13f1eb53 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 20 Aug 2024 16:29:51 +0200 Subject: [PATCH 5/5] Fixed Video upload not working --- slaeforms/app.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/slaeforms/app.py b/slaeforms/app.py index 7816fcd..0329efb 100644 --- a/slaeforms/app.py +++ b/slaeforms/app.py @@ -350,20 +350,7 @@ def sendpage(): # handle possible Video that was send if 'recordedVideo' in request.files: - print("------------------------trying to save the video-------------------------------") - #print("request.max_content_length:", request.max_content_length) - #print("request.max_form_memory_size:", request.max_form_memory_size) - #print("request.accept_mimetypes:", request.accept_mimetypes) - #print("request.args:", request.args) - #print("request.content_length:", request.content_length) video = request.files['recordedVideo'] - #print("request.data:", request.data) - #print("request.files:", request.files) - #print("request.form:", request.form) - print("video: ",video) - print("video.content_length: ", video.content_length) - #print("MIME type: ", video.mimetype) - #print("File name: ", video.filename) 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" @@ -371,7 +358,6 @@ def sendpage(): print("path: ",path) os.makedirs(UPLOAD_FOLDER, exist_ok=True) video.save(path) - print("------------------------END trying to save the video-------------------------------") if 'recordedVideo' in request.files: if hasattr(new_entry, "video_upload"): @@ -392,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