Initial commit

This commit is contained in:
2022-02-17 14:35:54 +01:00
commit abbaaab98a
43 changed files with 2079 additions and 0 deletions

View File

@ -0,0 +1 @@
default_app_config = 'apps.vragenlijst.apps.VragenlijstConfig'

View File

@ -0,0 +1,42 @@
from django.contrib import admin
from .models import Questionnaire, QuestionnaireTopic, QuestionnaireQuestion, QuestionnaireResponse, QuestionnaireStorage
# Register your models here.
@admin.register(Questionnaire)
class QuestionnaireAdmin(admin.ModelAdmin):
list_display = ('name', 'id',)
readonly_fields = ('created_at', 'updated_at')
@admin.register(QuestionnaireTopic)
class QuestionnaireTopicAdmin(admin.ModelAdmin):
list_display = ('name', 'order', 'questionnaire',)
ordering = ('order', )
readonly_fields = ('created_at', 'updated_at')
@admin.register(QuestionnaireQuestion)
class QuestionnaireQuestionAdmin(admin.ModelAdmin):
list_display = ('name', 'order', 'type', 'topic', 'questionnaire')
ordering = ('order', )
readonly_fields = ('created_at', 'updated_at')
def questionnaire(self, item):
return item.topic.questionnaire
@admin.register(QuestionnaireResponse)
class QuestionnaireResponseAdmin(admin.ModelAdmin):
list_display = ('id', 'created_at', 'questionnaire')
ordering = ('-created_at', )
readonly_fields = ('questionnaire','response','created_at', 'updated_at')
@admin.register(QuestionnaireStorage)
class QuestionnaireStorageAdmin(admin.ModelAdmin):
list_display = ('name', 'type', 'server')
ordering = ('name', )
readonly_fields = ('created_at', 'updated_at')

View File

@ -0,0 +1,11 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class VragenlijstConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.vragenlijst'
label = 'vragenlijst'
verbose_name = _('Questionnaire')
verbose_name_plural = _('Questionnaire')

View File

@ -0,0 +1,67 @@
# Generated by Django 4.0.2 on 2022-02-03 10:59
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Questionnaire',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('name', models.CharField(help_text='Name of the questionnaire.', max_length=200, verbose_name='Name')),
('description', models.TextField(blank=True, help_text='Enter a short description for this questionnaire.', null=True, verbose_name='Description')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this questionnaire has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this questionnaire has been updated', verbose_name='Date updated')),
],
options={
'verbose_name': 'Questionnaire',
'verbose_name_plural': 'Questionnaires',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='QuestionnaireTopic',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('name', models.CharField(help_text='Name of the questionnaire topic.', max_length=200, verbose_name='Name')),
('description', models.TextField(blank=True, help_text='Enter a short description for this questionnaire topic.', null=True, verbose_name='Description')),
('order', models.PositiveIntegerField(blank=True, verbose_name='Order')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this questionnaire topic has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this questionnaire topic has been updated', verbose_name='Date updated')),
('questionnaire', models.ForeignKey(help_text='The questionnaire topic for this questionnaire.', on_delete=django.db.models.deletion.CASCADE, to='vragenlijst.questionnaire', verbose_name='Questionnaire')),
],
options={
'verbose_name': 'Questionnaire topic',
'verbose_name_plural': 'Questionnaire topics',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='QuestionnaireQuestion',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('name', models.CharField(help_text='Name of the questionnaire topic.', max_length=200, verbose_name='Name')),
('type', models.CharField(choices=[('DATE', 'Date field'), ('NUMBER', 'Number field'), ('MULTIPLE', 'Multi options field'), ('SINGLE', 'Single option field'), ('TEXT', 'Single text line')], default='SINGLE', help_text='Question type', max_length=15, verbose_name='Type')),
('description', models.TextField(blank=True, help_text='Enter a short description for this questionnaire topic.', null=True, verbose_name='Description')),
('order', models.PositiveIntegerField(blank=True, verbose_name='Order')),
('choices', models.TextField(blank=True, help_text='Enter a short description for this questionnaire topic.', null=True, verbose_name='Choices')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this questionnaire topic has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this questionnaire topic has been updated', verbose_name='Date updated')),
('topic', models.ForeignKey(help_text='The questionnaire topic for this questionnaire.', on_delete=django.db.models.deletion.CASCADE, to='vragenlijst.questionnairetopic', verbose_name='Questionnaire topic')),
],
options={
'verbose_name': 'Questionnaire question',
'verbose_name_plural': 'Questionnaire questions',
'ordering': ['name'],
},
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.0.2 on 2022-02-03 11:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('vragenlijst', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='questionnairetopic',
name='questionnaire',
field=models.ForeignKey(help_text='The questionnaire topic for this questionnaire.', on_delete=django.db.models.deletion.CASCADE, related_name='topics', to='vragenlijst.questionnaire', verbose_name='Questionnaire'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.0.2 on 2022-02-03 12:21
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('vragenlijst', '0002_alter_questionnairetopic_questionnaire'),
]
operations = [
migrations.AlterField(
model_name='questionnairequestion',
name='topic',
field=models.ForeignKey(help_text='The questionnaire topic for this questionnaire.', on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='vragenlijst.questionnairetopic', verbose_name='Questionnaire topic'),
),
]

View File

@ -0,0 +1,49 @@
# Generated by Django 4.0.2 on 2022-02-08 10:01
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('vragenlijst', '0003_alter_questionnairequestion_topic'),
]
operations = [
migrations.CreateModel(
name='QuestionnaireStorage',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('name', models.CharField(help_text='Name of the questionnaire storage.', max_length=200, verbose_name='Name')),
('type', models.CharField(choices=[('WEBDAV', 'WebDAV')], default='WEBDAV', help_text='Storage type', max_length=15, verbose_name='Type')),
('server', models.CharField(help_text='Server url', max_length=200, verbose_name='Server')),
('username', models.CharField(help_text='Username', max_length=200, verbose_name='Username')),
('password', models.CharField(help_text='Password', max_length=200, verbose_name='Password')),
('path', models.CharField(help_text='Location on disk', max_length=200, verbose_name='Path')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this questionnaire storage has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this questionnaire storage has been updated', verbose_name='Date updated')),
],
options={
'verbose_name': 'Questionnaire storage',
'verbose_name_plural': 'Questionnaire storages',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='QuestionnaireResponse',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('response', models.TextField(help_text='Questionaire response in CSV', verbose_name='Response')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='The date and time this questionnaire response has been created', verbose_name='Date created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='The date and time this questionnaire response has been updated', verbose_name='Date updated')),
('questionnaire', models.ForeignKey(help_text='The questionnaire for this response.', on_delete=django.db.models.deletion.CASCADE, to='vragenlijst.questionnaire', verbose_name='Questionnaire')),
],
options={
'verbose_name': 'Questionnaire response',
'verbose_name_plural': 'Questionnaire responses',
'ordering': ['-created_at'],
},
),
]

View File

@ -0,0 +1,134 @@
from django.db import models
from django.db.models import Max, Value
from django.db.models.functions import Coalesce
from django.utils.translation import gettext_lazy as _
import uuid
from encrypted_model_fields.fields import EncryptedCharField
# Create your models here.
class Questionnaire(models.Model):
class Meta:
verbose_name = _('Questionnaire')
verbose_name_plural = _('Questionnaires')
ordering = ['name']
id = models.UUIDField(_('ID'), default=uuid.uuid4, editable=False, unique=True, primary_key=True)
name = models.CharField(_('Name'), max_length=200, help_text=_('Name of the questionnaire.'))
description = models.TextField(_('Description'), blank=True, null=True, help_text=_('Enter a short description for this questionnaire.'))
created_at = models.DateTimeField(_('Date created'), auto_now_add=True, help_text=_('The date and time this questionnaire has been created'))
updated_at = models.DateTimeField(_('Date updated'), auto_now=True, help_text=_('The date and time this questionnaire has been updated'))
@property
def allQuestions(self):
return QuestionnaireQuestion.objects.filter(topic__questionnaire=self.id)
def __str__(self):
"""str: Returns a readable string."""
return f'{self.name}'
class QuestionnaireTopic(models.Model):
class Meta:
verbose_name = _('Questionnaire topic')
verbose_name_plural = _('Questionnaire topics')
ordering = ['name']
id = models.UUIDField(_('ID'), default=uuid.uuid4, editable=False, unique=True, primary_key=True)
name = models.CharField(_('Name'), max_length=200, help_text=_('Name of the questionnaire topic.'))
questionnaire = models.ForeignKey(Questionnaire, verbose_name=Questionnaire._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The questionnaire topic for this questionnaire.'), related_name='topics')
description = models.TextField(_('Description'), blank=True, null=True, help_text=_('Enter a short description for this questionnaire topic.'))
order = models.PositiveIntegerField(_('Order'), blank=True)
created_at = models.DateTimeField(_('Date created'), auto_now_add=True, help_text=_('The date and time this questionnaire topic has been created'))
updated_at = models.DateTimeField(_('Date updated'), auto_now=True, help_text=_('The date and time this questionnaire topic has been updated'))
def save(self, *args, **kwargs):
if self.order is None:
self.order = QuestionnaireTopic.objects.filter(questionnaire=self.questionnaire).aggregate(neworder=Coalesce(Max('order'), Value(0)))['neworder'] + 1
super().save(*args, **kwargs)
def __str__(self):
"""str: Returns a readable string."""
return f'{self.name}'
class QuestionnaireQuestionTypes(models.TextChoices):
DATE = ('DATE', _('Date field'))
NUMBER = ('NUMBER', _('Number field'))
MULTIPLE = ('MULTIPLE', _('Multi options field'))
SINGLE = ('SINGLE', _('Single option field'))
TEXT = ('TEXT', _('Single text line'))
class QuestionnaireQuestion(models.Model):
class Meta:
verbose_name = _('Questionnaire question')
verbose_name_plural = _('Questionnaire questions')
ordering = ['name']
id = models.UUIDField(_('ID'), default=uuid.uuid4, editable=False, unique=True, primary_key=True)
name = models.CharField(_('Name'), max_length=200, help_text=_('Name of the questionnaire topic.'))
type = models.CharField(_('Type'), max_length=15, choices=QuestionnaireQuestionTypes.choices, default=QuestionnaireQuestionTypes.SINGLE, help_text=_('Question type'))
description = models.TextField(_('Description'), blank=True, null=True, help_text=_('Enter a short description for this questionnaire topic.'))
order = models.PositiveIntegerField(_('Order'), blank=True,)
choices = models.TextField(_('Choices'), blank=True, null=True, help_text=_('Enter the choices 1 per line.<br />Use a format like \'= [Text]\' for an \'anders\' option.<br />Use format \'[Text]=[Value]\' for different value for each choice '))
topic = models.ForeignKey(QuestionnaireTopic, verbose_name=QuestionnaireTopic._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The questionnaire topic for this questionnaire.'), related_name='questions')
created_at = models.DateTimeField(_('Date created'), auto_now_add=True, help_text=_('The date and time this questionnaire topic has been created'))
updated_at = models.DateTimeField(_('Date updated'), auto_now=True, help_text=_('The date and time this questionnaire topic has been updated'))
def save(self, *args, **kwargs):
if self.order is None:
self.order = QuestionnaireQuestion.objects.filter(topic__questionnaire=self.topic.questionnaire).aggregate(neworder=Coalesce(Max('order'), Value(0)))['neworder'] + 1
super().save(*args, **kwargs)
def choices_list(self):
return [choice.strip() for choice in self.choices.strip("\n").split('\n')]
def __str__(self):
"""str: Returns a readable string."""
return f'{self.name}'
class QuestionnaireResponse(models.Model):
class Meta:
verbose_name = _('Questionnaire response')
verbose_name_plural = _('Questionnaire responses')
ordering = ['-created_at']
id = models.UUIDField(_('ID'), default=uuid.uuid4, editable=False, unique=True, primary_key=True)
questionnaire = models.ForeignKey(Questionnaire, verbose_name=Questionnaire._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The questionnaire for this response.'))
response = models.TextField(_('Response'), help_text=_('Questionaire response in CSV'))
created_at = models.DateTimeField(_('Date created'), auto_now_add=True, help_text=_('The date and time this questionnaire response has been created'))
updated_at = models.DateTimeField(_('Date updated'), auto_now=True, help_text=_('The date and time this questionnaire response has been updated'))
class QuestionnaireStorageTypes(models.TextChoices):
WEBDAV = ('WEBDAV', _('WebDAV'))
class QuestionnaireStorage(models.Model):
class Meta:
verbose_name = _('Questionnaire storage')
verbose_name_plural = _('Questionnaire storages')
ordering = ['name']
id = models.UUIDField(_('ID'), default=uuid.uuid4, editable=False, unique=True, primary_key=True)
name = models.CharField(_('Name'), max_length=200, help_text=_('Name of the questionnaire storage.'))
type = models.CharField(_('Type'), max_length=15, choices=QuestionnaireStorageTypes.choices, default=QuestionnaireStorageTypes.WEBDAV, help_text=_('Storage type'))
server = models.CharField(_('Server'), max_length=200, help_text=_('Server url'))
username = EncryptedCharField(_('Username'), max_length=200, help_text=_('Username'))
password = EncryptedCharField(_('Password'), max_length=200, help_text=_('Password'))
path = models.CharField(_('Path'), max_length=200, help_text=_('Location on disk'))
created_at = models.DateTimeField(_('Date created'), auto_now_add=True, help_text=_('The date and time this questionnaire storage has been created'))
updated_at = models.DateTimeField(_('Date updated'), auto_now=True, help_text=_('The date and time this questionnaire storage has been updated'))

View File

@ -0,0 +1,382 @@
{% extends 'base.html' %}
<!-- Add this for inheritance -->
{% load replace %}
{% load choice_value %}
{% load i18n %}
{% load static %}
{% block content %}
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-12">
<div class="card px-0 pt-4 pb-0 mt-3 mb-3">
<h2 id="heading">Fill out the forms</h2>
<p>Fill all form field to go to next step</p>
<form id="msform" method="POST">
{% csrf_token %}
<!-- progressbar -->
<ul id="progressbar">
{% for topic in questionnaire.topics.all|dictsort:"order" %}
<li class="{% if forloop.first %} active {% endif %}"><strong><i class="fas fa-dot-circle"></i>{{ topic.name }}</strong></li>
{% endfor %}
</ul>
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<br> <!-- fieldsets -->
{% for topic in questionnaire.topics.all|dictsort:"order" %}
<fieldset>
<div class="form-card">
<div class="row">
<div class="col-7">
<h2 class="fs-title">{{topic.name}}:</h2>
<p>{{topic.description}}</p>
</div>
<div class="col-5">
<h2 class="steps text-right">Step {{forloop.counter}} - {{ questionnaire.topics.all.count }}</h2>
</div>
</div>
{% for question in topic.questions.all|dictsort:"order" %}
<br /><br />
<label class="fieldlabels" for="{{question.id}}">{{ question.description.strip }}</label><br />
{% if question.type == 'SINGLE' %}
{% for choice in question.choices_list %}
<input type="radio" id="{{question.id}}_{{ forloop.counter }}" name="{{question.id}}" value="{{choice.strip|choice_value}}" required="required">
<label for="{{question.id}}_{{forloop.counter}}">{{choice.strip|replace:"/(^=|=.*)/" }}</label>
{% if choice.strip|slice:"0:1" == "=" %}
<input type="text" name="{{question.id}}" id="{{question.id}}_{{ forloop.counter }}_anders" style="display:none" disabled="disabled">
<script>
$(document).ready(function(){
$('input[type="radio"][name="{{question.id}}"]').on('change',(e) => {
let choice = $(e.target)
let andersTXT = $('input[type="text"][name="{{question.id}}"]')
if (choice.val().slice(0,1) == '=') {
// Show
andersTXT.show()
andersTXT.prop('required',true)
andersTXT.prop('disabled',false)
} else {
// Hide
andersTXT.hide()
andersTXT.prop('required',false)
andersTXT.prop('disabled',true)
}
})
})
</script>
{%endif%}
{% endfor %}
{%elif question.type == 'NUMBER' %}
<input type="{{question.type|lower}}" id="{{question.id}}" name="{{question.id}}" value="" required="required">
{%elif question.type == 'DATE' %}
<input type="{{question.type|lower}}" id="{{question.id}}" name="{{question.id}}" value="" required="required">
{%elif question.type == 'TEXT' %}
<textarea rows="1" id="{{question.id}}" name="{{question.id}}" required="required"></textarea>
{% endif %}
{% endfor %}
</div>
{% if forloop.counter > 1 %}
<input type="button" name="previous" class="previous action-button-previous" value="Previous" />
{% endif %}
{% if forloop.last %}
<input type="submit" name="next" class="next action-button" value="Save" />
{%else %}
<input type="button" name="next" class="next action-button" value="Next" />
{%endif%}
</fieldset>
{% endfor %}
</form>
</div>
</div>
</div>
</div>
<style>
#grad1 {
background-color: #9C27B0;
background-image: linear-gradient(120deg, #FF4081, #81D4FA);
}
#msform {
text-align: center;
position: relative;
margin-top: 20px
}
#msform fieldset .form-card {
background: white;
border: 0 none;
border-radius: 0px;
box-shadow: 0 2px 2px 2px rgba(0, 0, 0, 0.2);
padding: 20px 40px 30px 40px;
box-sizing: border-box;
width: 94%;
margin: 0 3% 20px 3%;
position: relative
}
#msform fieldset {
background: white;
border: 0 none;
border-radius: 0.5rem;
box-sizing: border-box;
width: 100%;
margin: 0;
padding-bottom: 20px;
position: relative
}
#msform fieldset:not(:first-of-type) {
display: none
}
#msform fieldset .form-card {
text-align: left;
color: #000000;
}
#msform .action-button {
width: 100px;
background: skyblue;
font-weight: bold;
color: white;
border: 0 none;
border-radius: 0px;
cursor: pointer;
padding: 10px 5px;
margin: 10px 5px
}
#msform .action-button:hover,
#msform .action-button:focus {
box-shadow: 0 0 0 2px white, 0 0 0 3px skyblue
}
#msform .action-button-previous {
width: 100px;
background: #616161;
font-weight: bold;
color: white;
border: 0 none;
border-radius: 0px;
cursor: pointer;
padding: 10px 5px;
margin: 10px 5px
}
#msform .action-button-previous:hover,
#msform .action-button-previous:focus {
box-shadow: 0 0 0 2px white, 0 0 0 3px #616161
}
select.list-dt {
border: none;
outline: 0;
border-bottom: 1px solid #ccc;
padding: 2px 5px 3px 5px;
margin: 2px
}
select.list-dt:focus {
border-bottom: 2px solid skyblue
}
.card {
z-index: 0;
border: none;
border-radius: 0.5rem;
position: relative
}
.fs-title {
font-size: 25px;
color: #2C3E50;
margin-bottom: 10px;
font-weight: bold;
text-align: left
}
#progressbar {
margin-bottom: 30px;
overflow: hidden;
color: lightgrey
}
#progressbar .active {
color: #000000
}
#progressbar li {
list-style-type: none;
font-size: 12px;
width: {{menu_width}}%;
float: left;
position: relative
}
#progressbar li:before {
width: 50px;
height: 50px;
line-height: 45px;
display: block;
font-size: 18px;
color: #ffffff;
background: lightgray;
border-radius: 50%;
margin: 0 auto 10px auto;
padding: 2px
}
#progressbar li:after {
content: '';
width: 100%;
height: 2px;
background: lightgray;
position: absolute;
left: 0;
top: 25px;
z-index: -1
}
#progressbar li.active:before,
#progressbar li.active:after {
background: skyblue
}
.radio-group {
position: relative;
margin-bottom: 25px
}
.radio {
display: inline-block;
width: 204;
height: 104;
border-radius: 0;
background: lightblue;
box-shadow: 0 2px 2px 2px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
cursor: pointer;
margin: 8px 2px
}
.radio:hover {
box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.3)
}
.radio.selected {
box-shadow: 1px 1px 2px 2px rgba(0, 0, 0, 0.1)
}
.fit-image {
width: 100%;
object-fit: cover
}
.error {
/* border: solid 1px red; */
color:red;
font-weight: bold;
}
</style>
<script>
$(document).ready(function(){
var current_fs, next_fs, previous_fs; //fieldsets
var opacity;
var current = 1;
var steps = $("fieldset").length;
setProgressBar(current);
$(".next").click(function(){
current_fs = $(this).parent();
next_fs = $(this).parent().next();
let errors = false
current_fs.find('input,textarea').each((counter, element) => {
if (element.name && !element.disabled) {
if (!element.checkValidity()) {
jQuery('label[for=' + element.name + ']').addClass('error')
errors = true;
} else {
jQuery('label[for=' + element.name + ']').removeClass('error')
}
}
});
if (errors) {
return
}
//Add Class Active
$("#progressbar li").eq($("fieldset").index(next_fs)).addClass("active");
//show the next fieldset
next_fs.show();
//hide the current fieldset with style
current_fs.animate({opacity: 0}, {
step: function(now) {
// for making fielset appear animation
opacity = 1 - now;
current_fs.css({
'display': 'none',
'position': 'relative'
});
next_fs.css({'opacity': opacity});
},
duration: 500
});
setProgressBar(++current);
});
$(".previous").click(function(){
current_fs = $(this).parent();
previous_fs = $(this).parent().prev();
//Remove class active
$("#progressbar li").eq($("fieldset").index(current_fs)).removeClass("active");
//show the previous fieldset
previous_fs.show();
//hide the current fieldset with style
current_fs.animate({opacity: 0}, {
step: function(now) {
// for making fielset appear animation
opacity = 1 - now;
current_fs.css({
'display': 'none',
'position': 'relative'
});
previous_fs.css({'opacity': opacity});
},
duration: 500
});
setProgressBar(--current);
});
function setProgressBar(curStep){
var percent = parseFloat(100 / steps) * curStep;
percent = percent.toFixed();
$(".progress-bar")
.css("width",percent+"%")
}
// $(".submit").click(function(){
// return false;
// })
});
</script>
{% endblock %}

View File

@ -0,0 +1,39 @@
{% extends 'base.html' %}
<!-- Add this for inheritance -->
{% load i18n %}
{% load static %}
{% block content %}
{% if messages %}
<p>
{% for message in messages %}
{% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
{{ message }} <br />
{% endfor %}
{% endif %}
</p>
<p>Select the type of questionnaire you want to use</p>
{% if questionnaires %}
{% for questionnaire in questionnaires %}
<a href="{% url 'questionnaire' questionnaire.id %}" class="btn btn-primary btn-lg" role="button" aria-pressed="true">{{ questionnaire.name }}</a>
{% endfor %}
{% else %}
<p>No questionnaire are available.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,9 @@
from django import template
register = template.Library()
@register.filter
def choice_value ( string ):
if string[0] != '=' and '=' in string:
return string.split('=')[1].strip()
return string.strip()

View File

@ -0,0 +1,11 @@
import re
from django import template
register = template.Library()
@register.filter
def replace ( string, args ):
search = args.split(args[0])[1]
replace = args.split(args[0])[2]
return re.sub( search, replace, string )

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,8 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<uuid:questionnaire_id>/', views.questionnaire, name='questionnaire'),
]

View File

@ -0,0 +1,69 @@
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .models import Questionnaire, QuestionnaireResponse, QuestionnaireStorage
from django.conf import settings
from django.contrib import messages
import csv
from uuid import uuid4
from pathlib import Path
from storage.storage import Storage
# Create your views here.
def index(request):
# latest_question_list = Question.objects.order_by('-pub_date')[:5]
questionnaires = Questionnaire.objects.order_by('name')
context = {'questionnaires': questionnaires}
return render(request, 'vragenlijst/index.html', context)
def questionnaire(request, questionnaire_id):
if request.method == 'POST':
# As we should have a Django form, we could use te form validator. For now, everyhing is valid ;)
questionnaire = Questionnaire.objects.get(pk=questionnaire_id)
# Get all the questions in one list with a single query
allQuestions = {}
for question in list(questionnaire.allQuestions.order_by('order').values('id','name')):
allQuestions[str(question['id'])] = question['name']
# Store the response as a ';' seperated CSV file in order of the questionaire questions order
try:
csv_file = Path(f'{settings.HANZE_TEMP_CSV_STORAGE}/{questionnaire.name}-{uuid4()}.csv')
with open(csv_file, 'w') as csvfile:
filewriter = csv.writer(csvfile, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)
# Add headers
filewriter.writerow(['vraag', 'antwoord'])
for question in allQuestions:
if request.POST.get(question):
filewriter.writerow([allQuestions[question], request.POST[question]])
# Store the data also in a database
QuestionnaireResponse(questionnaire=questionnaire, response=csv_file.read_text()).save()
storageSettings = QuestionnaireStorage.objects.first()
# Move data to external storage
webdav = Storage(
storage_type=storageSettings.type,
url=storageSettings.server,
username=storageSettings.username,
password=storageSettings.password
)
webdav.upload_file(source=csv_file, destination=f'{storageSettings.path}/{csv_file.name}')
messages.success(request, 'Questionaire is saved to disk.')
except Exception as ex:
messages.error(request, f'Could not save the questionaire. Error: {ex}')
# Redirect to starting point
return HttpResponseRedirect('/vragenlijst/')
questionnaire = Questionnaire.objects.get(pk=questionnaire_id)
context = {'questionnaire': questionnaire, 'menu_width': 100 / questionnaire.topics.count()}
return render(request, 'vragenlijst/form.html', context)