Commit 3de82f61 authored by ZeMKI's avatar ZeMKI
Browse files

Study optimization

* code quality optimization: started to divide operations into the according model.
* solved a bug when you tried to edit a study with multiple/one choice AND open answer.
parent e5dea9b0
......@@ -3,6 +3,7 @@
namespace App\Helpers;
use Exception;
use File;
class Helper
{
......@@ -37,4 +38,23 @@ class Helper
$type = explode(';', $ini);
return $type[0];
}
/**
* get the preset images for the tokens
* @return array
*/
public static function getPresetImages(): array
{
$filesInFolder = File::files(storage_path('app/presets_tokens'));
$arrayOfFiles = [];
$i = 0;
foreach ($filesInFolder as $path) {
$arrayOfFiles[$i]['basename'] = pathinfo($path)['basename'];
$arrayOfFiles[$i]['dirname'] = url('/images/presets_tokens') . '/' . pathinfo($path)['basename'];
$i++;
}
return $arrayOfFiles;
}
}
......@@ -31,7 +31,7 @@ class StudyController extends Controller
public function create()
{
// if(!Auth::user()->cancreatestudies())abort(403,'You cannot create studies');
$arrayOfFiles = $this->getPresetImages();
$arrayOfFiles = Helper::getPresetImages();
$data['isedit'] = 'false';
$data['presetimages'] = $arrayOfFiles;
return view('study.create_edit', $data);
......@@ -50,7 +50,8 @@ class StudyController extends Controller
$newStudy->author = $request->author;
$newStudy->user_id = Auth::user()->id;
$newStudy->save();
$this->saveSorting($request, $newStudy);
Sorting::store($request, $newStudy);
// the role of the user who created this study within this study
// is the same as it has in others studies
......@@ -79,47 +80,17 @@ class StudyController extends Controller
{
$this->authorize($study);
//if(!Auth::user()->can('update-studies',$study->name)) abort(403,"You are not allowed to edit this study");
$data['study'] = $study;
$data['study']['tokens'] = $study->available_tokens;
$data['study']['sorting'] = $study->sortings;
$data['study']['tokens'] = $this->formatTokensForEdit($data['study']['tokens']);
$data['study']['q'] = $this->formatQuestionsForEdit($study);
$data['study']['tokens'] = Token::formatForEdit($data['study']['tokens']);
$data['study']['q'] = Question::formatForEdit($study);
$data['isedit'] = 'true';
$data['presetimages'] = $this->getPresetImages();
$data['presetimages'] = Helper::getPresetImages();
return view('study.create_edit', $data);
}
/**
* @param $study
* @return mixed
*/
public function formatQuestionsForEdit($study)
{
$study->tokens = $study->available_tokens;
$questionIds = [];
$questions['presort'] = [];
$questions['postsort'] = [];
foreach ($study->questions as $questionToEdit) {
if ($questionToEdit->detail === 'presort') {
$questionToEdit['answers'] = Answer::where('question_id', $questionToEdit->id)->get()->toArray();
array_push($questions['presort'], $questionToEdit);
} elseif ($questionToEdit->detail === 'postsort') {
$questionToEdit['answers'] = Answer::where('question_id', $questionToEdit->id)->get()->toArray();
array_push($questions['postsort'], $questionToEdit);
}
array_push($questionIds, $questionToEdit->id);
}
$questions['presort']['count'] = count($questions['presort']);
$questions['postsort']['count'] = count($questions['postsort']);
return $questions;
}
/**
* @param Study $study
......@@ -137,17 +108,14 @@ class StudyController extends Controller
if (empty($request->name)) {
return response()->json('Data are not valid', 422);
}
$study = Study::where('id', '=', $request->input('id'))->with('questions')->first();
if (!$study->isEditable() || !$study->allowedToInterview()) {
return back()->withInput();
}
// sorting
$description = $request->input('sorting.description');
$study->sortings()->detach();
// insert in details something according to the sorting
$study->sortings()->attach($request->get('sorting')['id'], ['details' => 'circles|' . $request->input('sorting.numberofcircles') . '||description|' . $description]);
//Auth::user()->studies()->attach($study->id, ['permission_id' => 4]);
Sorting::store($request,$study);
$study->description = $request->research_question;
$study->name = $request->name;
......@@ -156,11 +124,10 @@ class StudyController extends Controller
$study->save();
// watch out if we want more sortings
$study->sortings()->sync($request->get('sorting')['id']);
$this->removeTokensFromStudy($study);
$this->saveTokens($study, $request, $token);
......@@ -204,22 +171,7 @@ class StudyController extends Controller
}
/**
* @return array preset images
*/
public function getPresetImages(): array
{
$filesInFolder = File::files(storage_path('app/presets_tokens'));
$arrayOfFiles = [];
$i = 0;
foreach ($filesInFolder as $path) {
$arrayOfFiles[$i]['basename'] = pathinfo($path)['basename'];
$arrayOfFiles[$i]['dirname'] = url('/images/presets_tokens') . '/' . pathinfo($path)['basename'];
$i++;
}
return $arrayOfFiles;
}
/**
* @param Study $study
......@@ -293,17 +245,6 @@ class StudyController extends Controller
}
}
/**
* @param Request $request
* @param Study $newstudy
*/
private function saveSorting(Request $request, Study $newstudy): void
{
$newstudy->sortings()->detach();
// insert in details something according to the sorting
$newstudy->sortings()->attach($request->get('sorting')['id'], ['details' => 'circles|' . $request->input('sorting.numberofcircles') . '||description|' . $request->input('sorting.description')]);
}
/**
* @param Study $newstudy
*/
......@@ -351,25 +292,6 @@ class StudyController extends Controller
}
}
/**
* @param $tokens
* @return mixed
*/
public function formatTokensForEdit($tokens)
{
$tokensCount = count($tokens);
for ($i = 0; $i < $tokensCount; $i++) {
if (strpos($tokens[$i]['image_path'], 'presets') !== false) {
$path = $tokens[$i]['image_path'];
$tokens[$i]['image_path'] = mb_convert_encoding($path, 'HTML-ENTITIES', "UTF-8");
} else {
$path = storage_path('app/' . $tokens[$i]['image_path']);
$tokens[$i]['image_path'] = decrypt(file_get_contents($path));
}
}
return $tokens;
}
}
......
......@@ -21,4 +21,36 @@ class Question extends Model
{
return \App\Answer::where('question_id', '=', $this->id)->pluck('answer')->toArray();
}
/**
* @param $study
* @return mixed
*/
public static function formatForEdit($study)
{
$study->tokens = $study->available_tokens;
$questionIds = [];
$questions['presort'] = [];
$questions['postsort'] = [];
foreach ($study->questions as $questionToEdit) {
if ($questionToEdit->detail === 'presort') {
$questionToEdit['answers'] = Answer::where('question_id', $questionToEdit->id)->get()->toArray();
array_push($questions['presort'], $questionToEdit);
} elseif ($questionToEdit->detail === 'postsort') {
$questionToEdit['answers'] = Answer::where('question_id', $questionToEdit->id)->get()->toArray();
array_push($questions['postsort'], $questionToEdit);
}
array_push($questionIds, $questionToEdit->id);
}
$questions['presort']['count'] = count($questions['presort']);
$questions['postsort']['count'] = count($questions['postsort']);
return $questions;
}
}
......@@ -13,4 +13,16 @@ class Sorting extends Model
{
return $this->belongsToMany(\App\Study::class, 'study_sorting')->withTimestamps();
}
public static function store($request,$study)
{
$study->sortings()->detach();
// insert in details something according to the sorting
$study->sortings()->attach($request->get('sorting')['id'], ['details' => 'circles|' . $request->input('sorting.numberofcircles') . '||description|' . $request->input('sorting.description')]);
}
}
......@@ -99,4 +99,26 @@ class Token extends Model
$token->studies()->sync($newStudy->id);
}
/**
* @param $tokens
* @return mixed
*/
public static function formatForEdit($tokens)
{
$tokensCount = count($tokens);
for ($i = 0; $i < $tokensCount; $i++) {
if (strpos($tokens[$i]['image_path'], 'presets') !== false) {
$path = $tokens[$i]['image_path'];
$tokens[$i]['image_path'] = mb_convert_encoding($path, 'HTML-ENTITIES', "UTF-8");
} else {
$path = storage_path('app/' . $tokens[$i]['image_path']);
$tokens[$i]['image_path'] = decrypt(file_get_contents($path));
}
}
return $tokens;
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSpecialpermissionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('permissions_list', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 255);
$table->string('description', 255)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('permissions_list');
}
}
......@@ -1377,6 +1377,37 @@ __webpack_require__.r(__webpack_exports__);
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
 
 
/* harmony default export */ __webpack_exports__["default"] = ({
......@@ -1478,7 +1509,7 @@ __webpack_require__.r(__webpack_exports__);
this.postsort.number = this.studydata.q.postsort.count; // sorting
 
var detailsArray = this.studydata.sortings[0].pivot.details.split('||');
this.sorting.numberofcircles = detailsArray[0].substr(detailsArray[0].indexOf('|') + 1);
this.sorting.numberofcircles = _.parseInt(detailsArray[0].substr(detailsArray[0].indexOf('|') + 1));
if (detailsArray[1]) this.sorting.description = detailsArray[1].substr(detailsArray[1].indexOf('|') + 1);else this.sorting.description = "";
var self = this;
this.sorting.tokennumber = this.studydata.tokens.length;
......@@ -1513,7 +1544,8 @@ __webpack_require__.r(__webpack_exports__);
self.presort.questions[i].ismultiple = true;
 
_.times(self.studydata.q.presort[i].answers.length, function (x) {
self.presort.questions[i].answers[x] = self.studydata.q.presort[i].answers[x].answer.answer;
console.log(x);
if (self.studydata.q.presort[i].answers[x].answer.type == "multi") self.presort.questions[i].answers[x] = self.studydata.q.presort[i].answers[x].answer.answer;
});
}
 
......@@ -1524,7 +1556,7 @@ __webpack_require__.r(__webpack_exports__);
self.presort.questions[i].isonechoice = true;
 
_.times(self.studydata.q.presort[i].answers.length, function (x) {
self.presort.questions[i].answers[x] = self.studydata.q.presort[i].answers[x].answer.answer;
if (self.studydata.q.presort[i].answers[x].answer.type == "onechoice") self.presort.questions[i].answers[x] = self.studydata.q.presort[i].answers[x].answer.answer;
});
}
 
......@@ -1559,7 +1591,7 @@ __webpack_require__.r(__webpack_exports__);
self.postsort.questions[i].ismultiple = true;
 
_.times(self.studydata.q.postsort[i].answers.length, function (x) {
self.postsort.questions[i].answers[x] = self.studydata.q.postsort[i].answers[x].answer.answer;
if (self.studydata.q.postsort[i].answers[x].answer.type == "multi") self.postsort.questions[i].answers[x] = self.studydata.q.postsort[i].answers[x].answer.answer;
});
}
 
......@@ -1570,7 +1602,7 @@ __webpack_require__.r(__webpack_exports__);
self.postsort.questions[i].isonechoice = true;
 
_.times(self.studydata.q.postsort[i].answers.length, function (x) {
self.postsort.questions[i].answers[x] = self.studydata.q.postsort[i].answers[x].answer.answer;
if (self.studydata.q.postsort[i].answers[x].answer.type == "onechoice") self.postsort.questions[i].answers[x] = self.studydata.q.postsort[i].answers[x].answer.answer;
});
}
 
......@@ -1915,7 +1947,8 @@ __webpack_require__.r(__webpack_exports__);
}
},
normalizeanwers: function normalizeanwers(index, detail) {
var count = this[detail].questions[index].answers.length - this[detail].questions[index].numberofanswer;
var count = this[detail].questions[index].answers.length - _.parseInt(this[detail].questions[index].numberofanswer);
console.log(count);
 
if (count > 0) {
......@@ -13747,7 +13780,7 @@ var render = function() {
? "is-red"
: ""
},
[_vm._v("Study Name")]
[_vm._v("Study\n Name")]
),
_vm._v(" "),
_c("div", { staticClass: "control" }, [
......@@ -14507,7 +14540,7 @@ var render = function() {
steps: "1"
},
on: {
change: function($event) {
input: function($event) {
return _vm.normalizeanwers(
index,
"presort"
......@@ -14950,10 +14983,10 @@ var render = function() {
steps: "1"
},
on: {
change: function($event) {
input: function($event) {
return _vm.normalizeanwers(
index,
"presort"
"postsort"
)
}
},
{
"/js/app.js": "/js/app.js?id=ce5790be0386fcc4f2fa",
"/js/app.js": "/js/app.js?id=b43fc7edbcbb73a1cc77",
"/css/app.css": "/css/app.css?id=33e8c8db759d1aed6c23",
"/css/app_dompdf.css": "/css/app_dompdf.css?id=225174786ee9d00fe897",
"/js/manifest.js": "/js/manifest.js?id=844cdbfe9e6b6b56ae8f",
......
<template>
<div class="container" >
<div class="container">
<form enctype="multipart/form-data">
<section class="section" >
<section class="section">
<h1 class="title">Informations</h1>
<h6 class="subtitle has-text-danger">You can edit the study data until the first interview is done.</h6>
<div class="columns">
<div class="column">
<div class="field">
<label class="label" :class="response.indexOf(errormessages.studyname) > -1 ? 'is-red': ''">Study Name</label>
<label class="label" :class="response.indexOf(errormessages.studyname) > -1 ? 'is-red': ''">Study
Name</label>
<div class="control">
<input class="input" :class="response.indexOf(errormessages.studyname) > -1 ? 'is-danger': ''" type="text" placeholder="Study name" id="studyname" v-model="name" maxlength="80"/>
<input class="input"
:class="response.indexOf(errormessages.studyname) > -1 ? 'is-danger': ''"
type="text" placeholder="Study name" id="studyname" v-model="name"
maxlength="80"/>
</div>
</div>
</div>
......@@ -19,7 +23,9 @@
<div class="field">
<label class="label">Research Question</label>
<div class="control">
<textarea class="textarea" :class="response.indexOf(errormessages.research_question) > -1 ? 'is-danger': ''" placeholder="" v-model="research_question" maxlength="250"></textarea>
<textarea class="textarea"
:class="response.indexOf(errormessages.research_question) > -1 ? 'is-danger': ''"
placeholder="" v-model="research_question" maxlength="250"></textarea>
</div>
</div>
</div>
......@@ -28,7 +34,9 @@
<div class="field">
<label class="label">Author</label>
<div class="control">
<input class="input" :class="response.indexOf(errormessages.author) > -1 ? 'is-danger': ''" type="text" placeholder="" v-model="author" maxlength="40">
<input class="input"
:class="response.indexOf(errormessages.author) > -1 ? 'is-danger': ''"
type="text" placeholder="" v-model="author" maxlength="40">
</div>
</div>
</div>
......@@ -43,7 +51,8 @@
<div class="field">
<label class="label">Description</label>
<div class="control">
<input v-model="sorting.description" class="input" id="sortingDescription" type="text" placeholder="">
<input v-model="sorting.description" class="input" id="sortingDescription" type="text"
placeholder="">
</div>
</div>
</div>
......@@ -55,7 +64,7 @@
<label class="label">Sort Options</label>
<div class="has-background-dark level" style="padding: 15px">
<span class="icon">
<arrow-left class="has-text-light" />
<arrow-left class="has-text-light"/>
</span>
<div class="level-item has-text-centered">
<figure class="image is-128x128">
......@@ -63,11 +72,10 @@
</figure>
</div>
<span class="icon">
<arrow-right class="has-text-light" />
<arrow-right class="has-text-light"/>
</span>
</div>
</div>
</div>
......@@ -75,7 +83,9 @@
<div class="field">
<div class="control">
<b-field label="Number of circles">
<b-numberinput controls-position="compact" type="is-light" v-model="sorting.numberofcircles" min="2" max="8" :editable="editable" steps="1"></b-numberinput>
<b-numberinput controls-position="compact" type="is-light"
v-model="sorting.numberofcircles" min="2" max="8"
:editable="editable" steps="1"></b-numberinput>
</b-field>
</div>
</div>
......@@ -85,7 +95,9 @@
<div class="column is-one-quarter">
<b-field label="Number of tokens">
<b-numberinput controls-position="compact" type="is-light" v-model="sorting.tokennumber" id="numberoftokens" min="1" max="50" :editable="editable" steps="1"></b-numberinput>
<b-numberinput controls-position="compact" type="is-light" v-model="sorting.tokennumber"
id="numberoftokens" min="1" max="50" :editable="editable"
steps="1"></b-numberinput>
</b-field>
</div>
......@@ -93,29 +105,34 @@
<div class="columns is-multiline">
<div :class="sorting.tokennumber < 4? 'column is-one-quarter' :'column is-one-quarter'" v-for="(t,index) in sorting.tokens" :key="index">
<div class="card" >
<div class="token-img" >
<img :id="'token'+index" src="" alt="" style=" height: 100%;width: 100%;vertical-align: bottom; margin:auto;">
<div :class="sorting.tokennumber < 4? 'column is-one-quarter' :'column is-one-quarter'"
v-for="(t,index) in sorting.tokens" :key="index">
<div class="card">
<div class="token-img">
<img :id="'token'+index" src="" alt=""
style=" height: 100%;width: 100%;vertical-align: bottom; margin:auto;">
</div>
<div class="card-content">
<div class="content">
<label class="label is-small">Token name</label>
<input class="input is-small " type="text" v-model="t.name" />
<input class="input is-small " type="text" v-model="t.name"/>
<label class="label is-small mt-5">Preset tokens</label>
<v-select :options="preset" label="basename" v-model="t.file" @input="settokenfrompreset(index)">
<v-select :options="preset" label="basename" v-model="t.file"
@input="settokenfrompreset(index)">
<template slot="label" slot-scope="label">
{ option.basename }}
</template>
<template slot="option" slot-scope="option">
<img :src="option.dirname" style="max-width: 25px;padding: 0px;margin: 0 auto;display:inline;"/>
<span class="fa" :value="option.dirname"></span>
<img :src="option.dirname"
style="max-width: 25px;padding: 0px;margin: 0 auto;display:inline;"/>
<span class="fa" :value="option.dirname"></span>
{{ option.basename }}
</template>
</v-select>
</div>
<b-field class="file" >
<b-upload v-model="t.file" @input="settokenname(index)" drag-drop accept="image/*">
<b-field class="file">
<b-upload v-model="t.file" @input="settokenname(index)" drag-drop
accept="image/*">
<a class="button is-danger">
<span>Click to upload</span>
</a>
......@@ -139,7 +156,8 @@
<div class="column">
<div class="field">
<b-field label="Pre-Sort Questions">
<b-numberinput controls-position="compact" type="is-light" v-model="presort.number" min="0" max="20" :editable="editable" steps="1"></b-numberinput>
<b-numberinput controls-position="compact" type="is-light" v-model="presort.number"
min="0" max="20" :editable="editable" steps="1"></b-numberinput>
</b-field>
</div>
<div class="field" v-for="(q,index) in presort.questions">
......@@ -148,11 +166,13 @@
<input v-model="q.question" class="input " type="text" placeholder="">
</div>
<label class="checkbox">
<input type="checkbox" id="active" checked v-model="q.ismultiple" :disabled="q.isscale" @input="unsetifmultiple(index,'presort')">
<input type="checkbox" id="active" checked v-model="q.ismultiple" :disabled="q.isscale"
@input="unsetifmultiple(index,'presort')">
Multiple Choice
</label>
<label class="checkbox">
<input type="checkbox" id="active" checked v-model="q.isonechoice" :disabled="q.isscale" @input="unsetifonechoice(index,'presort')">
<input type="checkbox" id="active" checked v-model="q.isonechoice" :disabled="q.isscale"
@input="unsetifonechoice(index,'presort')">
One Choice
</label>
<label class="checkbox">
......@@ -160,18 +180,21 @@
Open Answer
</label>
<label class="checkbox">
<input type="checkbox" id="active" checked v-model="q.isscale" @input="unsetifscale(index,'presort')">
<input type="checkbox" id="active" checked v-model="q.isscale"
@input="unsetifscale(index,'presort')">