Giter Site home page Giter Site logo

thefragebogen / thefragebogen Goto Github PK

View Code? Open in Web Editor NEW
9.0 4.0 5.0 1.05 MB

A standalone questionnaire system that only requires a HTML5-capable web browser.

Home Page: http://www.TheFragebogen.de

License: MIT License

Makefile 0.15% JavaScript 88.53% HTML 11.32%
questionnaire subjective-tests crowdsourcing experiments

thefragebogen's Introduction

TheFragebogen

TheFragebogen: logo

TheFragebogen is a HTML5 framework for creating stand-alone questionnaires, i.e., running a web browser alone.
Questionnaires are implemented as single-page applications and thus can be loaded from a local file or from a web server. The collected data can be downloaded to the executing machine or uploaded to a web server.

The Features

  • Provides a large, extendable set of scales (e.g., Likert scale).
  • Requires only web browser while a web server is optional.
  • Supports playback of audio and video.
  • Support for free-hand writing or drawing (exported as PNG).
  • Supports interaction with other systems via AJAX or WebSockets.

Data is exported as CSV: either as local download or by sending it to a HTTP-server.

Most important TheFragebogen is implemented completely in JavaScript.
It can thus be easily extended and modified while no infrastructure is mandatory (e.g., web server, database, and network connectivity).

Requirements are:

ATTENTION: Some features might not be available/usable depending on the web browser features.

Getting started

Install required software:

  1. Install NodeJS to get npm: https://nodejs.org/en/download/
  2. Install grunt via npm: npm install -g grunt-cli
  3. Install git as command-line interface: https://git-scm.com/download/

Build TheFragebogen:

  1. Clone git repository: git clone https://github.com/TheFragebogen/TheFragebogen
  2. Enter repository: cd TheFragebogen
  3. Install dependencies via npm: npm install
  4. Build TheFragebogen: grunt
  5. Check out the included examples to see how to get started using TheFragebogen: see examples/.
  6. Check out the included feasibility studies that show how TheFragebogen could be extended: see examples_feasibility/. ATTENTION: Please note that the feasibility studies are not ready for use.

Build the documentation:

  1. Build documentation: grunt doc.
  2. Open documentation in your favorite web browser doc/index.html.

The Concept

TheFragebogen follows the paper metaphor, i.e., a paper-based questionnaire is actually a sequence of paper sheets, which each consists of several items. Items consist, in fact, of a question and a scale.
Sheets are represented in TheFragebogen by so-called Screens and items by so-called QuestionnaireItems. In TheFragebogen, a questionnaires consists of one or more Screens while only one Screen is shown at a time. A Screen encapsulates a specific functionality, such as presenting of items, waiting for some time, or exporting data. For presenting items or HTML content, TheFragebogen provides ScreenUIElements that can present generic HTML content (UIElementHTML) or items (QuestionnaireItems).

The lifecycle of a questionnaire is handled by the ScreenController, which organizes the presentation of all Screens and also data export.

TheFragebogen is completely implemented using OOP.

DEVELOPERS: Information for developers can be found in CODING.md.

The Data

The data is exported as CSV while each element is wrapped in quotation marks.

The data consists of five columns.

For each row:

  • Column 1 contains the index of the screen.
  • Column 2 contains the name of the class (for QuestionnaireItems or Screens).
  • Column 3 contains the question (might be empty or a generic identifier).
  • Column 4 contains the answer options (might be empty).
  • Column 5 contains the answer(s) (might contain an array). Might contain text/numbers, JSON, or a data URI (i.e., multimedia content).

The data files can be opened using any compatible program (e.g., text editor, spreadsheet editor). TheFragebogen-csv-viewer.html is a viewer application that also decodes images encoded as data URI.

License

TheFragebogen is licensed under MIT License.

thefragebogen's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

thefragebogen's Issues

Measure screen duration for every screen

Sometimes, the time how long a screen is shown needs to be measured.
For ScreenUIElements this can be easily done using a questionnaireItem.
However, for other screens this functionality would need be implemented manually again.
A generic solution would be desired.

A screen that provides a back button would be great.

Here is some not working demo code.

function ScreenUIElementsJump() {
    ScreenUIElements.apply(this, arguments);
    this.backCallback = null;
    this.sentBy = null;
    this.jumpIndex=1;
}
ScreenUIElementsJump.prototype = Object.create(ScreenUIElements.prototype);
ScreenUIElementsJump.prototype.constructor = ScreenUIElementsJump;

ScreenUIElementsJump.prototype.setOnBackCallback = function(backCallback){
    this.backCallback = backCallback;
};

ScreenUIElementsJump.prototype._sendOnBackCallback = function(){
    this.sentBy = -1;
    this.backCallback();
}

ScreenUIElementsJump.prototype.createUI = function () {
    this.node = document.createElement("div");
    this.node.className = this.className;

    for (var index in this.elements) {
        if (this.elements[index].createUI !== undefined) {
            var node = this.elements[index].createUI();
            if (node !== undefined && node !== null) this.node.appendChild(node);
        } else
            TheFragebogen.logger.warn(this.constructor.name + ".createUI():", "Element[" + index + "] has no 'createUI' method");
    }


    var button = document.createElement("input");
    button.type = "button";
    button.value = "Replay video";
    button.onclick = (this._sendOnBackCallback).bind(this);
    this.node.appendChild(button);

    this.node.appendChild(document.createElement("br"));

    var button = document.createElement("input");
    button.type = "button";
    button.value = "Next";
    button.onclick = (this._sendOnReadyCallback).bind(this);
    button.style.marginTop = "20px";
    this.node.appendChild(button);

    return this.node;
};

Maintenance: fix check for undefined variable

Do not use typeof(a) === "undefined" , but rather use a === undefined.

Occurences:
src/screen_uielements.js
22: if (typeof(className) !== "string" || typeof(className) === "undefined" || className === null) {

src/questionnaireitem_write.js
31: this.backgroundImg = typeof(backgroundImg) !== "undefined" ? backgroundImg : "";

examples/example_uielements.html
32: if (typeof(questionnaireItems) === "undefined") {

examples/example_screen_sequential.html
47: if (typeof(screenController) === "undefined") {

examples/developer_template.html
65: if (typeof(screenController) === "undefined") {

examples/example_dynamic.html
23: if (typeof(screenController) === "undefined") {

examples/example_paginate.html
56: if (typeof(screenController) === "undefined") {

examples/example_media.html
56: if (typeof(screenController) === "undefined") {

examples/example_print.html
55: if (typeof(screens) === "undefined") {

examples/example_iframe.html
80: if (typeof(screenController) === "undefined") {

examples/example_basic.html
93: if (typeof(screenController) === "undefined") {

examples/example_websockets.html
63: if (typeof(screenController) === "undefined") {

tests/manual_test.html
114: if (typeof(screenController) === "undefined") {

CODING.md
61:7. typeof always returns a string: (typeof(undefined) === "undefined") => true

Create a Mailinglist?

Freely available services are:

  • librelist.com
  • freelists.com

Are there alternatives?

Remove UglifyJS

UglifyJS does not support ES6.
As their is not easy out-of-the-box replacement, the a minified version will not be included in ES6+ releases.

CSV-Data export: use JSON.stringify?

The data exported so far, the concat-operator is used implicitely in getDataCSV.
For complex objects this should be changed to JSON.stringify().

Code is in ScreenController.prototype.requestDataArray = function() {

Constructors: throw exceptions on invalid input.

So far, only TheFragebogen's logger.error was called.
However, this does not result in failure of tests for example and it also would be nice to inform the developer that something is wrong.

throw new Error("Something weird happened"); instead.
Only question: change this in constructors or change logger.error to emit the failure?

Build process: Update build tools versions

NPM already complains about potential security issues.
Should upgrade all version to most recent.

In addition, the copy of grunt-concat-in-order should be removed and the one from npm used.
We just need to wait for the next release.

Store changelog of QuestionnaireItem's answers

So far only the final answer given to a questionnaireitem is stored and then later available for export.
For some situations, it might be interesting to see

  • if the answer was changed and what the previous answer(s) were and
  • at which point in times the change(s) occurred.

This would require rewritting the QuestionnaireItem.setAnswer() as well as QuestionnaireItem.getData().
ATTENTION: QuestionnaireItem.setAnswer() is at the moment called by the UI as well as the internal processes to restore data.
This would need to be changed to determine the source of the call to setAnswer().
In the best case use two separate methods.

#62 needs to be addressed first.

Named parameters (Map-based constructors)

Type of issue

  • Bug
  • Feature request
  • Maintenance / code cleanup

In TheFragebogen parameter-based constructors are used: new QuestionnaireItemDefinedOne(undefined, "Are you ready?", true, ["Yes"]).
This is, however, difficult to read for users - especially as optional parameters need to be defined.
It would be great to enable use of so called named parameters: new QuestionnaireItemDefinedOne({question: "Are you ready?", options: {"Yes}, required: true}).

As alternative a factory pattern could be used.

See: http://2ality.com/2011/11/keyword-parameters.html

Requirement:

  • backward compatibility: parameter-based constructors should remain usable.

Move to ES6 (code only)

TODOs:

  1. use let instead of var
  2. use arrow functions where possible

The issue for moving to ES6 class structure is #28.

Provide JSON export?

For some applications it might be easier to import JSON rather than CSV (e.g., processing data directly in the web browser).

So far all classes storing data provide the method ScreenController.getDataCSV() that converts the stored data into CSV (single or multiple lines).
To implement JSON export, adding ScreenController.getDataJSON() might be an option.

In addition, the data format needs to be configurable.
At the moment it is set in https://github.com/TheFragebogen/TheFragebogen/blob/master/src/screencontroller.js#L52

Remove `getData()`, `setData()`, and `_checkData()`

Type of issue

  • Bug
  • Feature request
  • Maintenance / code cleanup

General information

The method getData(), setData(), and _checkData() were implemented to restore a questionnaire from LocalStorage.
However, the implementation was never ready for use and was therefore removed before open source release.
These method should therefore also be removed and, if needed again, re-implementation should take place.

And rename screen.getDataCSV() to screen.getData().

Do after ES6 upgrade (i.e., formatting).

ScreenController.init and ScreenController.addScreen: contain duplicated code.

The callbacks for the screens are set in both functions.

    for (i = 0; i < this.screen.length; i++) {
        if (this.screen[i].setGetDataCallback instanceof Function) {
            this.screen[i].setGetDataCallback((this.requestDataCSV).bind(this));
        }
        if (this.screen[i].setGetRawDataCallback instanceof Function) {
            this.screen[i].setGetRawDataCallback((this.requestDataArray).bind(this));
        }
        if (this.screen[i].setOnReadyStateChangedCallback instanceof Function) {
            this.screen[i].setOnReadyStateChangedCallback((this.nextScreen).bind(this));
        }
    }

Implement regression tests

Type of issue

  • Bug
  • Feature request
  • Maintenance / code cleanup

Implement full-blown test cases that also check the exported data (i.e., screenController.requestDataAraray()).
This will allow to spot regressions.

Building documentation aborted due to warnings

Not sure if I'm doing something wrong here, I followed the "Getting Started" guide and everything else worked fine.

PS C:\Users\gabriel\Documents\Fragebogen\TheFragebogen> grunt doc
Running "includereplace:dist" (includereplace) task
Processed 43 files
Running "revision" task
HEAD at revision b883a790163947f6fc1fa51fe0d178629f83947b

Running "concat_in_order:your_target" (concat_in_order) task
File "thefragebogen.js" created.

Running "uglify:dist" (uglify) task
>> 1 file created 185.14 kB โ†’ 125.63 kB

Running "run:jsdoc" (run) task
Unknown command-line option "d=doc".

JSDoc 3.5.5 (Thu, 14 Sep 2017 02:51:54 GMT)

Options:
    -a, --access <value>         Only
                                 display
                                 symbols
                                 with
                                 the
                                 given
                                 access:
                                 "package",
                                 public",
                                 "protected",
                                 "private"
                                 or
                                 "undefined",
                                 or
                                 "all"
                                 for
                                 all
                                 access
                                 levels.
                                 Default:
                                 all
                                 except
                                 "private"
    -c, --configure <value>      The
                                 path
                                 to
                                 the
                                 configuration
                                 file.
                                 Default:
                                 path/to/jsdoc/conf.json
    -d, --destination <value>    The
                                 path
                                 to
                                 the
                                 output
                                 folder.
                                 Default:
                                 ./out/
    --debug                      Log
                                 information
                                 for
                                 debugging
                                 JSDoc.
    -e, --encoding <value>       Assume
                                 this
                                 encoding
                                 when
                                 reading
                                 all
                                 source
                                 files.
                                 Default:
                                 utf8
    -h, --help                   Print
                                 this
                                 message
                                 and
                                 quit.
    --match <value>              When
                                 running
                                 tests,
                                 only
                                 use
                                 specs
                                 whose
                                 names
                                 contain
                                 <value>.
    --nocolor                    When
                                 running
                                 tests,
                                 do
                                 not
                                 use
                                 color
                                 in
                                 console
                                 output.
    -p, --private                Display
                                 symbols
                                 marked
                                 with
                                 the
                                 @private
                                 tag.
                                 Equivalent
                                 to
                                 "--access
                                 all".
                                 Default:
                                 false
    -P, --package <value>        The
                                 path
                                 to
                                 the
                                 project's
                                 package
                                 file.
                                 Default:
                                 path/to/sourcefiles/package.json
    --pedantic                   Treat
                                 errors
                                 as
                                 fatal
                                 errors,
                                 and
                                 treat
                                 warnings
                                 as
                                 errors.
                                 Default:
                                 false
    -q, --query <value>          A
                                 query
                                 string
                                 to
                                 parse
                                 and
                                 store
                                 in
                                 jsdoc.env.opts.query.
                                 Example:
                                 foo=bar&baz=true
    -r, --recurse                Recurse
                                 into
                                 subdirectories
                                 when
                                 scanning
                                 for
                                 source
                                 files
                                 and
                                 tutorials.
    -R, --readme <value>         The
                                 path
                                 to
                                 the
                                 project's
                                 README
                                 file.
                                 Default:
                                 path/to/sourcefiles/README.md
    -t, --template <value>       The
                                 path
                                 to
                                 the
                                 template
                                 to
                                 use.
                                 Default:
                                 path/to/jsdoc/templates/default
    -T, --test                   Run
                                 all
                                 tests
                                 and
                                 quit.
    -u, --tutorials <value>      Directory
                                 in
                                 which
                                 JSDoc
                                 should
                                 search
                                 for
                                 tutorials.
    -v, --version                Display
                                 the
                                 version
                                 number
                                 and
                                 quit.
    --verbose                    Log
                                 detailed
                                 information
                                 to
                                 the
                                 console
                                 as
                                 JSDoc
                                 runs.
    -X, --explain                Dump
                                 all
                                 found
                                 doclet
                                 internals
                                 to
                                 console
                                 and
                                 quit.

Visit http://usejsdoc.org for more information.
There are no input files to process.
Warning: non-zero exit code 1 Use --force to continue.

Aborted due to warnings.
PS C:\Users\gabriel\Documents\Fragebogen\TheFragebogen>

Implement ScreenPaginate

Pagination is already implemented (see PaginateUIButton).
However, setting it up for a child of Screen is quite annoying.

Solution: Implement a class ScreenPaginate that provides this functionality and can be used for inheritance.
Also affects: ScreenUIElements and ScreenDataPreview

Storing a questionnaire's progress in HTML5's local storage

At the moment, TheFragebogen does not store it's progress persistently.
Thus, closing the web page will result in a loss of all so far gathered data.
This issue might be avoided to a certain degree by storing the data at a regular interval.
However, continuing a questionnaire is not easily possible using this method.

It would be nice to persistently store the current progress (incl. gathered data) in HTML5's local storage [1].
On start, TheFragebogen can check if their is stored data available and if it's fits to the questionnaire (ie. data is in line with the loaded configuration).
If so the data can be restored and TheFragebogen would continue from the last stored data point.

Support for serializing and de-serializing should be already implemented for nearly all components of TheFragebogen using the getData()-method and setData()-method after an object was created.

[1] https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API

[Feature] Rework Web Socket client

Code should be working, but it is not nice.

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Observer</title>
    </head>
    <body>
        <div id="content">
            <fieldset>
                <legend>Server Location</legend>
                <div>
                    <label>URL:</label>
                    <input type="text" id="serverUrl" value="ws://localhost:8080">
                    <button id="connectButton">Open</button>
                    <button id="disconnectButton" style="display:none">Close</button>
                </div>
                <div>
                    <label>Status:</label>
                    <span id="connectionStatus">CLOSED</span>
                </div>
            </fieldset>
            <fieldset id="requestArea">
                <legend>Request</legend>
                <div>
                    <textarea id="sendMessage"></textarea>
                </div>
                <div>
                    <button id="sendButton" disabled="disabled">Send</button>
                    [Shortcut] Ctr + Enter
                </div>
            </fieldset>
            <fieldset id="messageArea">
                <legend>Message Log <button id="clearButton">Clear</button></legend>
                <div id="messages"></div>
            </fieldset>
        </div>
    </body>
    <script type="text/javascript">
        //Variables
        var ws = null;
        var connected = false;

        var serverUrl = $('#serverUrl');
        var connectionStatus = $('#connectionStatus');
        var sendMessage = $('#sendMessage');
        var messages = $('#messages');

        var connectButton = $('#connectButton');
        var disconnectButton = $('#disconnectButton');
        var sendButton = $('#sendButton');
        var clearButton = $('#clearButton');

        //Shortcut to send message
        var isCtrl;
        sendMessage.keyup(function (e) {
            if (e.which == 17) isCtrl = false;
        }).keydown(function (e) {
            if (e.which == 17) isCtrl = true;
            if (e.which == 13 && isCtrl == true) {
                sendButton.click();
                return false;
            }
        });

        //Click events
        connectButton.click(function (e) {
            close();
            open();
        });

        disconnectButton.click(function (e) {
            close();
        });

        sendButton.click(function (e) {
            var msg = $('#sendMessage').val();
            var data = {type: "message", timeStamp: Date.now(), origin: "you", data: msg}
            addMessage(data);
            ws.send(msg);
        });

        clearButton.click(function (e) {
            clearLog();
        });

        //Functions
        var open = function () {
            var url = serverUrl.val();
            ws = new WebSocket(url);
            ws.onopen = onOpen;
            ws.onclose = onClose;
            ws.onmessage = onMessage;
            ws.onerror = onError;

            connectionStatus.text('OPENING ...');
            serverUrl.attr('disabled', 'disabled');
            connectButton.hide();
            disconnectButton.show();
        }

        var close = function () {
            if (ws) {
                console.log('CLOSING ...');
                ws.close();
            }
        }

        var addMessage = function (data) {
            var text = typeof (data) == "object" ? ("(" + new Date(data.timeStamp).toLocaleTimeString() + ") " + data.origin + " sent '" + data.data + "'") : data;
            messages.prepend($('<pre>').text(text));
        }

        var updateMessages = function (msgs) {
            clearLog();
            for (var i in msgs)
                addMessage(msgs[i]);
            console.log("Messages updated!");
        }

        var clearLog = function () {
            messages.html('');
        }

        //Event listeners
        var onOpen = function () {
            console.log('OPENED: ' + serverUrl.val());
            connected = true;
            connectionStatus.text('OPENED');
            sendMessage.removeAttr('disabled');
            sendButton.removeAttr('disabled');
        };

        var onClose = function () {
            console.log('CLOSED: ' + serverUrl.val());
            ws = null;
            connected = false;
            connectionStatus.text('CLOSED');

            serverUrl.removeAttr('disabled');
            connectButton.show();
            disconnectButton.hide();
            sendMessage.attr('disabled', 'disabled');
            sendButton.attr('disabled', 'disabled');
        };

        var onMessage = function (event) {
            try {
                var data = JSON.parse(event.data);
            } catch (err) {
                console.log("Try with an Object stringified");
                var data = event
            }

            try {
                if (JSON.parse(data.data) instanceof Array)
                    updateMessages(JSON.parse(data.data));
            } catch (err) {
                addMessage(data);
            }
        };

        var onError = function (event) {
            alert(event.data);
        }
    </script>
</html>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.