aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Fuhse <fuhse@data-quest.de>2024-11-25 10:13:55 +0000
committerRasmus Fuhse <fuhse@data-quest.de>2024-11-25 10:13:55 +0000
commitcdc7050e5be7a0813b4634ca2aaf33fff311ff52 (patch)
tree8fe6a73dab7c0e43b3158a50b1d517bcfe2d120d
parent773d384678349e16ccc41c888a30b10736860816 (diff)
Resolve "Automatisierte Angaben in Fragebögen"
Closes #4259 Merge request studip/studip!3156
-rw-r--r--app/controllers/questionnaire.php1
-rw-r--r--app/views/questionnaire/edit.php1
-rw-r--r--app/views/questionnaire/question_types/automated_data/_evaluation_part.php81
-rw-r--r--app/views/questionnaire/question_types/automated_data/answer.php54
-rw-r--r--app/views/questionnaire/question_types/automated_data/evaluation.php110
-rw-r--r--db/migrations/6.0.33_add_automated_data_questiontype.php36
-rw-r--r--lib/classes/JsonApi/RouteMap.php2
-rw-r--r--lib/classes/JsonApi/Routes/DatafieldsIndex.php38
-rw-r--r--lib/classes/JsonApi/SchemaMap.php1
-rw-r--r--lib/classes/JsonApi/Schemas/Datafield.php42
-rw-r--r--lib/models/QuestionnaireAutomatedData.php182
-rw-r--r--public/assets/images/icons/blue/question-automation.svg1
-rw-r--r--public/assets/images/icons/blue/question-automation2.svg1
-rw-r--r--resources/assets/stylesheets/scss/questionnaire.scss4
-rw-r--r--resources/vue/components/questionnaires/AutomatedDataEdit.vue61
15 files changed, 615 insertions, 0 deletions
diff --git a/app/controllers/questionnaire.php b/app/controllers/questionnaire.php
index c136178..9f16442 100644
--- a/app/controllers/questionnaire.php
+++ b/app/controllers/questionnaire.php
@@ -18,6 +18,7 @@ class QuestionnaireController extends AuthenticatedController
class_exists('LikertScale');
class_exists('RangeScale');
class_exists('QuestionnaireInfo');
+ class_exists('QuestionnaireAutomatedData');
PageLayout::setHelpKeyword('Basis/Votings');
}
diff --git a/app/views/questionnaire/edit.php b/app/views/questionnaire/edit.php
index e5ef007..cd73fbc 100644
--- a/app/views/questionnaire/edit.php
+++ b/app/views/questionnaire/edit.php
@@ -13,6 +13,7 @@ foreach (get_declared_classes() as $class) {
if (
is_subclass_of($class, QuestionType::class)
&& !isset($questiontypes[$class])
+ && isset($class::getEditingComponent()[0])
) {
$questiontypes[$class] = [
'name' => $class::getName(),
diff --git a/app/views/questionnaire/question_types/automated_data/_evaluation_part.php b/app/views/questionnaire/question_types/automated_data/_evaluation_part.php
new file mode 100644
index 0000000..d2b7bac
--- /dev/null
+++ b/app/views/questionnaire/question_types/automated_data/_evaluation_part.php
@@ -0,0 +1,81 @@
+<?
+/**
+ * @var QuestionnaireQuestion $question
+ * @var QuestionnaireAnswer[] $answers
+ * @var $filtered string
+ * @var $description string
+ * @var $answerdata array
+ * @var $options array
+ */
+
+?>
+
+<div class="description_container">
+ <div class="icon_container">
+ <?= Icon::create('tan2', Icon::ROLE_INFO) ?>
+ </div>
+ <div class="description">
+ <?= formatReady($description) ?>
+ </div>
+</div>
+
+
+<table class="default nohover">
+ <tbody>
+ <? $countAnswers = $question->questionnaire->countAnswers() ?>
+ <? foreach ($options as $key => $answer) : ?>
+ <tr>
+ <? $percentage = $countAnswers && isset($answerdata[$key]) ? round(count((array) $answerdata[$key]) / $countAnswers * 100) : 0 ?>
+
+ <td style="text-align: right; background-size: <?= $percentage ?>% 100%; background-position: right center; background-image: url('<?= Assets::image_path("vote_lightgrey.png") ?>'); background-repeat: no-repeat;" width="50%">
+ <strong><?= htmlReady($answer) ?></strong>
+ </td>
+
+ <td style="white-space: nowrap;">
+ <? if (!empty($filtered) && $filtered == $key) : ?>
+ <a href=""
+ title="<?= _('Zeige wieder alle Ergebnisse ohne Filterung an.') ?>"
+ onclick="STUDIP.Questionnaire.removeFilter('<?= htmlReady($question['questionnaire_id']) ?>'); return false;">
+ <?= Icon::create('filter2')->asImg(['class' => 'text-bottom']) ?>
+ (<?= $percentage ?>% | <?= (int) count((array) $answerdata[$key]) ?>/<?= $countAnswers ?>)
+ </a>
+ <? else : ?>
+ <a href=""
+ onclick="STUDIP.Questionnaire.addFilter('<?= htmlReady($question['questionnaire_id']) ?>', '<?= htmlReady($question->getId()) ?>', '<?= $key ?>'); return false;"
+ title="<?= _('Zeige nur Ergebnisse von Personen an, die diese Option gewählt haben.') ?>">
+ (<?= $percentage ?>% | <?= isset($answerdata[$key]) ? count((array) $answerdata[$key]) : 0 ?>/<?= $countAnswers ?>)
+ </a>
+ <? endif ?>
+ </td>
+
+ <td width="50%">
+ <? if (!$question->questionnaire['anonymous'] && isset($answerdata[$key]) && $answerdata[$key]) : ?>
+
+ <? $users = SimpleCollection::createFromArray(
+ User::findMany($answerdata[$key])); ?>
+
+ <? foreach ($answerdata[$key] as $index => $user_id) : ?>
+
+ <? $user = $users->findOneBy('user_id', $user_id); ?>
+
+ <? if ($user) : ?>
+ <a href="<?= URLHelper::getLink(
+ 'dispatch.php/profile',
+ ['username' => $user->username]
+ ) ?>">
+ <?= Avatar::getAvatar($user_id, $user->username)->getImageTag(
+ Avatar::SMALL,
+ ['title' => $user->getFullname('no_title')]
+ ) ?>
+ <? if (count($answerdata[$key]) < 4) : ?>
+ <?= htmlReady($user->getFullname('no_title')) ?>
+ <? endif ?>
+ </a>
+ <? endif ?>
+ <? endforeach ?>
+ <? endif ?>
+ </td>
+ </tr>
+ <? endforeach ?>
+ </tbody>
+</table>
diff --git a/app/views/questionnaire/question_types/automated_data/answer.php b/app/views/questionnaire/question_types/automated_data/answer.php
new file mode 100644
index 0000000..53ee222
--- /dev/null
+++ b/app/views/questionnaire/question_types/automated_data/answer.php
@@ -0,0 +1,54 @@
+<?
+/**
+ * @var QuestionnaireQuestion $question
+ */
+$data = [];
+$user = User::findCurrent();
+if ($question['questiondata']['geschlecht']) {
+ $map = [
+ 0 => _('unbekannt'),
+ 1 => _('männlich'),
+ 2 => _('weiblich'),
+ 3 => _('divers')
+ ];
+ $data[] = _('Geschlecht:') . ' ' . $map[$user['geschlecht']];
+}
+if ($question['questiondata']['studienfach']) {
+ $studienfach = [];
+ foreach ($user->studycourses as $studycourse) {
+ $studienfach[] = $studycourse->studycourse_name;
+ }
+ $data[] = _('Studienfach:') . ' ' . (implode(', ', $studienfach) ?: _('Kein Eintrag'));
+}
+if ($question['questiondata']['studiengang']) {
+ $studiengang = [];
+ foreach ($user->studycourses as $studycourse) {
+ $studiengang[] = $studycourse->studycourse_name . ' ' . $studycourse->degree_name;
+ }
+ $data[] = _('Studiengang:') . ' ' . (implode(', ', $studiengang) ?: _('Kein Eintrag'));
+}
+if ($question['questiondata']['studiengangfachsemester']) {
+ $studiengang = [];
+ foreach ($user->studycourses as $studycourse) {
+ $studiengang[] = $studycourse->studycourse_name . ' ' . $studycourse->degree_name . ' ' . $studycourse->semester;
+ }
+ $data[] = _('Studiengang und Fachsemester:') . ' ' . (implode(', ', $studiengang) ?: _('Kein Eintrag'));
+}
+if (isset($question['questiondata']['datafields'])) {
+ foreach ($question['questiondata']['datafields'] as $datafield_id) {
+ $datafield = DataField::find($datafield_id);
+ if ($datafield) {
+ $entry = DatafieldEntryModel::findOneBySQL('range_id = :user_id AND datafield_id = :datafield_id', [
+ 'user_id' => $user->getId(),
+ 'datafield_id' => $datafield_id
+ ]);
+ $data[] = $datafield['name'] . ': ' . ($entry ? $entry['content'] : _('Kein Eintrag'));
+ }
+ }
+}
+?>
+<? if ($question->questionnaire['anonymous']) : ?>
+ <?= MessageBox::info(_('Die folgenden Daten werden in diesem Fragebogen automatisch erfasst. Die Teilnahme an diesem Fragebogen erfolgt grundsätzlich anonym, aber eventuell können die automatisch erfassten Daten zu einer Deanonymisierung führen.'), array_map('htmlReady', $data)) ?>
+<? else : ?>
+ <?= MessageBox::info(_('Die folgenden Daten werden in diesem Fragebogen automatisch erfasst:'), array_map('htmlReady', $data)) ?>
+<? endif ?>
diff --git a/app/views/questionnaire/question_types/automated_data/evaluation.php b/app/views/questionnaire/question_types/automated_data/evaluation.php
new file mode 100644
index 0000000..16b8f46
--- /dev/null
+++ b/app/views/questionnaire/question_types/automated_data/evaluation.php
@@ -0,0 +1,110 @@
+<?
+/**
+ * @var QuestionnaireQuestion $question
+ * @var QuestionnaireAnswer[] $answers
+ * @var $filtered string
+ */
+?>
+
+<? if ($question['questiondata']['geschlecht']) : ?>
+ <? $options = [
+ 0 => _('unbekannt'),
+ 1 => _('männlich'),
+ 2 => _('weiblich'),
+ 3 => _('divers')
+ ];
+ $answerdata = [];
+ foreach ($answers as $answer) {
+ if (isset($answer['answerdata']['geschlecht'])) {
+ $answerdata[$answer['answerdata']['geschlecht']][] = $answer['user_id'];
+ }
+ }
+ ?>
+ <?= $this->render_partial('questionnaire/question_types/automated_data/_evaluation_part.php', [
+ 'options' => $options,
+ 'description' => _('Geschlecht'),
+ 'answerdata' => $answerdata,
+ ]) ?>
+<? endif ?>
+
+<? if ($question['questiondata']['studienfach']) : ?>
+ <? $options = [];
+ $answerdata = [];
+ foreach ($answers as $answer) {
+ if (isset($answer['answerdata']['studienfach'])) {
+ foreach ($answer['answerdata']['studienfach'] as $studienfach) {
+ if (!in_array($studienfach, $options)) {
+ $options[$studienfach] = $studienfach;
+ }
+ $answerdata[$studienfach][] = $answer['user_id'];
+ }
+ }
+ }
+ ?>
+ <?= $this->render_partial('questionnaire/question_types/automated_data/_evaluation_part.php', [
+ 'options' => $options,
+ 'description' => _('Studiengang'),
+ 'answerdata' => $answerdata,
+ ]) ?>
+<? endif ?>
+
+<? if ($question['questiondata']['studiengang']) : ?>
+ <? $options = [];
+ $answerdata = [];
+ foreach ($answers as $answer) {
+ if (isset($answer['answerdata']['studiengang'])) {
+ foreach ($answer['answerdata']['studiengang'] as $studiengang) {
+ if (!in_array($studiengang, $options)) {
+ $options[$studiengang] = $studiengang;
+ }
+ $answerdata[$studiengang][] = $answer['user_id'];
+ }
+ }
+ }
+ ?>
+ <?= $this->render_partial('questionnaire/question_types/automated_data/_evaluation_part.php', ['options' => $options, 'description' => _('Studiengang'), 'answerdata' => $answerdata]) ?>
+<? endif ?>
+
+<? if ($question['questiondata']['studiengangfachsemester']) : ?>
+ <? $options = [];
+ $answerdata = [];
+ foreach ($answers as $answer) {
+ if (isset($answer['answerdata']['studiengangfachsemester'])) {
+ foreach ($answer['answerdata']['studiengangfachsemester'] as $studiengangfachsemester) {
+ if (!in_array($studiengangfachsemester, $options)) {
+ $options[$studiengangfachsemester] = $studiengangfachsemester;
+ }
+ $answerdata[$studiengangfachsemester][] = $answer['user_id'];
+ }
+ }
+ }
+ ?>
+ <?= $this->render_partial('questionnaire/question_types/automated_data/_evaluation_part.php', [
+ 'options' => $options, 'description' => _('Studiengang und Fachsemester'),
+ 'answerdata' => $answerdata,
+ ]) ?>
+<? endif ?>
+
+<? if (isset($question['questiondata']['datafields'])) :
+ foreach ($question['questiondata']['datafields'] as $datafield_id) :
+ $datafield = DataField::find($datafield_id);
+ if ($datafield) : ?>
+ <? $options = [];
+ $answerdata = [];
+ foreach ($answers as $answer) {
+ if (isset($answer['answerdata']['datafields'][$datafield_id])) {
+ if (!in_array($answer['answerdata']['datafields'][$datafield_id], $options)) {
+ $options[$answer['answerdata']['datafields'][$datafield_id]] = $answer['answerdata']['datafields'][$datafield_id];
+ }
+ $answerdata[$answer['answerdata']['datafields'][$datafield_id]][] = $answer['user_id'];
+ }
+ }
+ ?>
+ <?= $this->render_partial('questionnaire/question_types/automated_data/_evaluation_part.php', [
+ 'options' => $options,
+ 'description' => $datafield['name'],
+ 'answerdata' => $answerdata,
+ ]) ?>
+ <? endif ?>
+ <? endforeach ?>
+<? endif ?>
diff --git a/db/migrations/6.0.33_add_automated_data_questiontype.php b/db/migrations/6.0.33_add_automated_data_questiontype.php
new file mode 100644
index 0000000..7a318fe
--- /dev/null
+++ b/db/migrations/6.0.33_add_automated_data_questiontype.php
@@ -0,0 +1,36 @@
+<?php
+
+class AddAutomatedDataQuestionType extends Migration
+{
+
+ public function description()
+ {
+ return 'Adds a new question type Automated Data. You can disable this question type by changing the config QUESTIONNAIRE_AUTOMATED_DATA_PERM.';
+ }
+
+ public function up()
+ {
+ $query = "INSERT IGNORE INTO `config` (`field`, `value`, `type`, `range`, `section`, `mkdate`, `chdate`, `description`)
+ VALUES (:name, :value, :type, :range, :section, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), :description)";
+
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([
+ ':name' => 'QUESTIONNAIRE_AUTOMATED_DATA_PERM',
+ ':description' => 'Ab welchem Status (autor, tutor, dozent, admin, root) darf man den Fragetyp Automatik in Fragebögen einbauen?',
+ ':range' => 'global',
+ ':type' => 'string',
+ ':section' => 'global',
+ ':value' => 'autor'
+ ]);
+ }
+
+ public function down()
+ {
+ $query = "DELETE FROM `config`
+ WHERE `field` = 'QUESTIONNAIRE_AUTOMATED_DATA_PERM' ";
+ DBManager::get()->exec($query);
+ $query = "DELETE FROM `config_values`
+ WHERE `field` = 'QUESTIONNAIRE_AUTOMATED_DATA_PERM' ";
+ DBManager::get()->exec($query);
+ }
+}
diff --git a/lib/classes/JsonApi/RouteMap.php b/lib/classes/JsonApi/RouteMap.php
index ec3668d..c7a6c89 100644
--- a/lib/classes/JsonApi/RouteMap.php
+++ b/lib/classes/JsonApi/RouteMap.php
@@ -158,6 +158,8 @@ class RouteMap
$group->get('/studip/properties', Routes\Studip\PropertiesIndex::class);
+ $group->get('/datafields', Routes\DatafieldsIndex::class);
+
if (\PluginManager::getInstance()->getPlugin(\CoursewareModule::class)) {
$group->get('/public/courseware/{link_id}/courseware-structural-elements/{id}', Routes\Courseware\PublicStructuralElementsShow::class);
$group->get('/public/courseware/{link_id}/courseware-structural-elements', Routes\Courseware\PublicStructuralElementsIndex::class);
diff --git a/lib/classes/JsonApi/Routes/DatafieldsIndex.php b/lib/classes/JsonApi/Routes/DatafieldsIndex.php
new file mode 100644
index 0000000..b966c71
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/DatafieldsIndex.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace JsonApi\Routes;
+
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Message\ResponseInterface as Response;
+use JsonApi\JsonApiController;
+
+/**
+ * List all the semesters.
+ */
+class DatafieldsIndex extends JsonApiController
+{
+ protected $allowedFilteringParameters = ['object_type'];
+ protected $allowedPagingParameters = ['offset', 'limit'];
+
+ public function __invoke(Request $request, Response $response, $args)
+ {
+ [$offset, $limit] = $this->getOffsetAndLimit();
+
+ $params = $this->getQueryParameters();
+ $filtering = $params->getFilteringParameters();
+ if (isset($filtering['object_type'])) {
+ $datafields = \DataField::getDataFields($filtering['object_type']);
+ } else {
+ $datafields = \DataField::getDataFields();
+ }
+ $datafields = array_filter(
+ $datafields,
+ fn($field) => $field->accessAllowed()
+ );
+
+ return $this->getPaginatedContentResponse(
+ array_slice($datafields, $offset, $limit),
+ count($datafields)
+ );
+ }
+}
diff --git a/lib/classes/JsonApi/SchemaMap.php b/lib/classes/JsonApi/SchemaMap.php
index 50d6760..c651b87 100644
--- a/lib/classes/JsonApi/SchemaMap.php
+++ b/lib/classes/JsonApi/SchemaMap.php
@@ -35,6 +35,7 @@ class SchemaMap
\CourseMember::class => Schemas\CourseMember::class,
\CourseDate::class => Schemas\CourseEvent::class,
\CourseExDate::class => Schemas\CourseEvent::class,
+ \DataField::class => Schemas\Datafield::class,
\Degree::class => Schemas\Degree::class,
\FeedbackElement::class => Schemas\FeedbackElement::class,
\FeedbackEntry::class => Schemas\FeedbackEntry::class,
diff --git a/lib/classes/JsonApi/Schemas/Datafield.php b/lib/classes/JsonApi/Schemas/Datafield.php
new file mode 100644
index 0000000..74b3b9c
--- /dev/null
+++ b/lib/classes/JsonApi/Schemas/Datafield.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace JsonApi\Schemas;
+
+use Neomerx\JsonApi\Contracts\Schema\ContextInterface;
+use JsonApi\Schemas\SchemaProvider;
+
+class Datafield extends SchemaProvider
+{
+ const TYPE = 'datafield';
+
+ public function getId($datafield): ?string
+ {
+ return $datafield->getId();
+ }
+
+ public function getAttributes($datafield, ContextInterface $context): iterable
+ {
+ return [
+ 'name' => (string) $datafield->name,
+ 'object_type' => $datafield->object_type,
+ 'object_class' => $datafield->object_class,
+ 'institut_id' => $datafield->institut_id,
+ 'priority' => $datafield->priority,
+ 'type' => $datafield->type,
+ 'typeparam' => $datafield->typeparam,
+ 'is_required' => (bool) $datafield->is_required,
+ 'default_value' => $datafield->default_value,
+ 'is_userfilter' => (bool) $datafield->is_userfilter,
+ 'mkdate' => date('c', $datafield->mkdate),
+ 'chdate' => date('c', $datafield->chdate)
+ ];
+ }
+
+ /**
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function getRelationships($datafield, ContextInterface $context): iterable
+ {
+ return [];
+ }
+}
diff --git a/lib/models/QuestionnaireAutomatedData.php b/lib/models/QuestionnaireAutomatedData.php
new file mode 100644
index 0000000..5b48392
--- /dev/null
+++ b/lib/models/QuestionnaireAutomatedData.php
@@ -0,0 +1,182 @@
+<?php
+
+class QuestionnaireAutomatedData extends QuestionnaireQuestion implements QuestionType
+{
+ public static function getIcon(bool $active = false) : Icon
+ {
+ return Icon::create(static::getIconShape(), $active ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO);
+ }
+
+ /**
+ * Returns the shape of the icon of this QuestionType
+ */
+ public static function getIconShape(): string
+ {
+ return 'question-automation';
+ }
+
+ public static function getName(): string
+ {
+ return _('Automatik');
+ }
+
+ public static function getEditingComponent(): array
+ {
+ if ($GLOBALS['perm']->have_perm(Config::get()->QUESTIONNAIRE_AUTOMATED_DATA_PERM)) {
+ return ['AutomatedDataEdit', ''];
+ } else {
+ //in this case the question_type is not allowed:
+ return [];
+ }
+ }
+
+ public function beforeStoringQuestiondata($questiondata)
+ {
+ return $questiondata;
+ }
+
+ public function getDisplayTemplate(): ?Flexi\Template
+ {
+ if ($GLOBALS['user']->id !== 'nobody') {
+ $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views'));
+ $template = $factory->open('questionnaire/question_types/automated_data/answer');
+ $template->set_attribute('question', $this);
+ return $template;
+ } else {
+ return null;
+ }
+ }
+
+ public function createAnswer(): QuestionnaireAnswer
+ {
+ $answer = $this->getMyAnswer();
+ $answerdata = [];
+ if ($GLOBALS['user']->id !== 'nobody') {
+ $user = User::findCurrent();
+ if ($this['questiondata']['geschlecht']) {
+ $answerdata['geschlecht'] = $user->geschlecht;
+ }
+ if ($this['questiondata']['studienfach']) {
+ $answerdata['studienfach'] = [];
+ foreach ($user->studycourses as $studycourse) {
+ $answerdata['studienfach'][] = $studycourse->studycourse_name;
+ }
+ }
+ if ($this['questiondata']['studiengang']) {
+ $answerdata['studiengang'] = [];
+ foreach ($user->studycourses as $studycourse) {
+ $answerdata['studiengang'][] = $studycourse->studycourse_name . ' ' . $studycourse->degree_name;
+ }
+ }
+ if ($this['questiondata']['studiengangfachsemester']) {
+ $answerdata['studiengangfachsemester'] = [];
+ foreach ($user->studycourses as $studycourse) {
+ $answerdata['studiengangfachsemester'][] = $studycourse->studycourse_name . ' ' . $studycourse->degree_name . ' ' . $studycourse['semester'];
+ }
+ }
+ foreach ($this['questiondata']['datafields'] as $datafield_id) {
+ $datafieldentry = DatafieldEntryModel::findOneBySQL('range_id = :user_id AND datafield_id = :datafield_id', [
+ 'datafield_id' => $datafield_id,
+ 'user_id' => $user->getId()
+ ]);
+ $answerdata['datafields'][$datafield_id] = $datafieldentry['content'];
+ }
+ }
+
+ $answer->answerdata = $answerdata;
+ return $answer;
+ }
+
+ public function getUserIdsOfFilteredAnswer($answer_option): array
+ {
+ $user_ids = [];
+ foreach ($this->answers as $answer) {
+ $answerData = $answer['answerdata']->getArrayCopy();
+ if (in_array($answer_option, (array) $answerData['answers'])) {
+ $user_ids[] = $answer['user_id'];
+ }
+ }
+ return $user_ids;
+ }
+
+ public function getResultTemplate($only_user_ids = null): Flexi\Template
+ {
+ $answers = $this->answers;
+ if ($only_user_ids !== null) {
+ foreach ($answers as $key => $answer) {
+ if (!in_array($answer['user_id'], $only_user_ids)) {
+ unset($answers[$key]);
+ }
+ }
+ }
+ $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views'));
+ $template = $factory->open('questionnaire/question_types/automated_data/evaluation');
+ $template->set_attribute('question', $this);
+ $template->set_attribute('answers', $answers);
+ return $template;
+ }
+
+ public function getResultArray(): array
+ {
+ $output = [];
+
+ $options = [];
+ if ($this['questiondata']['geschlecht']) {
+ $options['geschlecht'] = _('Geschlecht');
+ }
+ if ($this['questiondata']['studienfach']) {
+ $options['studienfach'] = _('Studienfach');
+ }
+ if ($this['questiondata']['studiengang']) {
+ $options['studiengang'] = _('Studiengang');
+ }
+ if ($this['questiondata']['studiengangfachsemester']) {
+ $options['studiengangfachsemester'] = _('Studiengang und Fachsemester');
+ }
+ foreach ($this['questiondata']['datafields'] as $datafield_id) {
+ $datafield = DataField::find($datafield_id);
+ if ($datafield) {
+ $options[$datafield_id] = (string) $datafield['name'];
+ }
+ }
+
+ $map = [
+ 0 => _('unbekannt'),
+ 1 => _('männlich'),
+ 2 => _('weiblich'),
+ 3 => _('divers')
+ ];
+
+ foreach ($options as $key => $option) {
+ $answerOption = [];
+ $countNobodys = 0;
+
+ foreach ($this->answers as $answer) {
+ $answerData = $answer['answerdata']->getArrayCopy();
+
+ if ($answer['user_id'] && $answer['user_id'] != 'nobody') {
+ $userId = $answer['user_id'];
+ } else {
+ $countNobodys++;
+ $userId = _('unbekannt').' '.$countNobodys;
+ }
+
+ if (isset($answerData[$key])) {
+ if (is_array($answerData[$key])) {
+ $answerOption[$userId] = implode('|', $answerData[$key]);
+ } else {
+ $answerOption[$userId] = $map[$answerData[$key]];
+ }
+ } elseif(strlen($key) === 32 && isset($answerData['datafields']) && isset($answerData['datafields'][$key])) {
+ $answerOption[$userId] = $answerData['datafields'][$key];
+ } else {
+ $answerOption[$userId] = '';
+ }
+ }
+
+ $output[$option] = $answerOption;
+ }
+
+ return $output;
+ }
+}
diff --git a/public/assets/images/icons/blue/question-automation.svg b/public/assets/images/icons/blue/question-automation.svg
new file mode 100644
index 0000000..de55c00
--- /dev/null
+++ b/public/assets/images/icons/blue/question-automation.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><path d="M26.65 13H4v38h56V13zM8 17h11.48c.33 1.28.52 2.62.52 4 0 7.45-5.1 13.7-12 15.48zm0 30v-6.43c.36-.07.73-.16 1.08-.25l1.84 2.95c.24.38.72.54 1.13.36l2.25-.93c.42-.17.65-.62.55-1.06l-.78-3.39c.7-.41 1.37-.86 2.02-1.35l2.83 2.02c.37.26.87.22 1.19-.1l1.72-1.72c.32-.32.36-.82.1-1.19l-2.02-2.83c.49-.64.94-1.32 1.35-2.02l3.39.78c.44.1.89-.13 1.06-.55l.93-2.25c.17-.42.02-.9-.36-1.13l-2.95-1.84c.2-.78.36-1.57.47-2.38l3.43-.57c.44-.07.77-.46.77-.91v-2.44c0-.45-.33-.84-.77-.91l-3.43-.57c-.06-.44-.14-.87-.22-1.3h32.43v16.03s-.05.02-.08.04l-2.83 2.02c-.64-.49-1.31-.94-2.02-1.35l.78-3.39c.1-.44-.13-.89-.55-1.06l-2.25-.93a.915.915 0 0 0-1.13.36l-1.84 2.95c-.78-.2-1.57-.36-2.38-.47l-.57-3.43a.924.924 0 0 0-.91-.77h-2.44c-.45 0-.84.33-.91.77l-.57 3.43c-.81.11-1.6.27-2.38.47l-1.84-2.95a.91.91 0 0 0-1.13-.36l-2.25.93c-.42.17-.65.62-.55 1.06l.78 3.39c-.7.41-1.37.86-2.02 1.35l-2.83-2.02a.926.926 0 0 0-1.19.1l-1.72 1.72c-.32.32-.36.82-.1 1.19l2.02 2.83c-.49.64-.94 1.32-1.35 2.02l-3.39-.78c-.44-.1-.89.13-1.06.55l-.93 2.25c-.17.42-.02.9.36 1.13l2.95 1.84c-.09.36-.18.72-.25 1.08H8Zm48 0H25.52C27.3 40.1 33.54 35 41 35c6.89 0 12.75 4.37 15 10.48z" fill="#28497c" fill-rule="evenodd"/></svg> \ No newline at end of file
diff --git a/public/assets/images/icons/blue/question-automation2.svg b/public/assets/images/icons/blue/question-automation2.svg
new file mode 100644
index 0000000..ae65a05
--- /dev/null
+++ b/public/assets/images/icons/blue/question-automation2.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"><path fill="none" d="M0 0h64v64H0z"/><path d="M7 47h22.346l.077 1.53-.074 1.47H4V37.635l1.936-1.597L7 35.46zm53-9.145-1.884-2.122-1.116.366V47H36.4l.058.526-3.303 1.529-.096.945H60zM18.8 14H4v16.992l1.413.996L7 31.274V17h16.771l-.261-1.246-4.672-.96zM60 29.058l-1.41.857-1.59.766V17H28.976l-.254-.987L28.415 14H60z" fill="#28497C"/><path d="M28.977 17c2.253 8.85 10.288 15.392 19.84 15.392 2.909 0 5.677-.607 8.183-1.71v5.417a22.3 22.3 0 0 1-4.311 1.089l-.96 4.67a30 30 0 0 1-5.826 0l-.96-4.67a25.7 25.7 0 0 1-5.426-1.454l-3.167 3.565a30 30 0 0 1-5.045-2.913l1.504-4.525a25.6 25.6 0 0 1-3.973-3.973l-4.525 1.505a30 30 0 0 1-2.913-5.046l3.566-3.166A22 22 0 0 1 23.772 17zM60 29.058v8.797l-1.884-2.121-1.116.365v-5.418l1.59-.766zM28.722 16.013 28.415 14H18.8l.039.794 4.67.96.263 1.246h5.205z" fill="#28497C"/><path d="M7 31.274a15.8 15.8 0 0 1 3.576-1.166l.931-3.518a22 22 0 0 1 5.7 0l.931 3.518a18.8 18.8 0 0 1 5.163 1.88l2.976-2.096a22 22 0 0 1 4.365 3.663l-1.548 3.294a18.8 18.8 0 0 1 2.748 4.758l3.626.307c.518 1.652.84 3.359.932 5.086h-7.054c-.767-7.598-7.19-13.537-14.989-13.537-2.685 0-5.207.704-7.357 1.997zM36.4 47h-7.054l.078 1.53-.075 1.47h3.71l.096-.945 3.303-1.529zM5.413 31.988 7 31.274v4.186l-1.065.578L4 37.636v-6.644z" fill="#28497C"/></svg> \ No newline at end of file
diff --git a/resources/assets/stylesheets/scss/questionnaire.scss b/resources/assets/stylesheets/scss/questionnaire.scss
index fde7d32..f4d28e3 100644
--- a/resources/assets/stylesheets/scss/questionnaire.scss
+++ b/resources/assets/stylesheets/scss/questionnaire.scss
@@ -138,6 +138,10 @@ $width: 270px;
text-align: center;
}
}
+
+ .automated-data-checkbox-list {
+ margin-top: 20px;
+ }
}
.questionnaire_results {
diff --git a/resources/vue/components/questionnaires/AutomatedDataEdit.vue b/resources/vue/components/questionnaires/AutomatedDataEdit.vue
new file mode 100644
index 0000000..f37bba9
--- /dev/null
+++ b/resources/vue/components/questionnaires/AutomatedDataEdit.vue
@@ -0,0 +1,61 @@
+<template>
+ <div>
+ <studip-message-box type="info" :hideClose="false">
+ {{ $gettext('Die folgenden Daten können automatisch erfasst werden. Achtung: Diese Daten können zu einer Deanonymisierung der Teilnehmenden führen. Die Teilnehmenden sehen einen Hinweis, dass und welche Daten von ihnen erfasst werden.') }}
+ </studip-message-box>
+ <div class="automated-data-checkbox-list">
+ <label>
+ <input v-autofocus type="checkbox" v-model="val_clone.geschlecht" true-value="1" false-value="0">
+ {{ $gettext('Geschlecht') }}
+ </label>
+ <label>
+ <input type="checkbox" true-value="1" v-model="val_clone.studienfach" false-value="0">
+ {{$gettext('Studienfach')}}
+ </label>
+ <label>
+ <input type="checkbox" true-value="1" v-model="val_clone.studiengang" false-value="0">
+ {{$gettext('Studiengang')}}
+ </label>
+ <label>
+ <input type="checkbox" true-value="1" v-model="val_clone.studiengangfachsemester" false-value="0">
+ {{$gettext('Studiengang-Fachsemester')}}
+ </label>
+ <label v-for="datafield in datafields" :key="datafield.datafield_id">
+ <input type="checkbox" v-model="val_clone.datafields" :value="datafield.datafield_id">
+ {{ datafield.name }}
+ </label>
+ </div>
+ </div>
+</template>
+
+<script>
+import axios from 'axios';
+import { QuestionnaireComponent } from '../../mixins/QuestionnaireComponent';
+
+export default {
+ name: 'automated-data-edit',
+ mixins: [ QuestionnaireComponent ],
+ data() {
+ return {
+ datafields: []
+ }
+ },
+ created() {
+ this.setDefaultValues({
+ geschlecht: 0,
+ studienfach: 0,
+ studiengang: 0,
+ studiengangfachsemester: 0,
+ datafields: []
+ });
+ axios.get(STUDIP.URLHelper.getURL(`jsonapi.php/v1/datafields?filter[object_type]=user`)).then((response) => {
+ for (let studiengang of response.data.data) {
+ this.datafields.push({
+ datafield_id: studiengang.id,
+ name: studiengang.attributes.name
+ });
+ }
+ });
+ }
+}
+</script>