Passed
Push — master ( afb2ec...e80e7a )
by Julito
09:13
created

lp_upload_quiz_main()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 58
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 43
nc 4
nop 0
dl 0
loc 58
rs 9.232
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use ChamiloSession as Session;
6
7
/**
8
 * Upload quiz: This script shows the upload quiz feature.
9
 */
10
$help_content = 'exercise_upload';
11
require_once __DIR__.'/../inc/global.inc.php';
12
13
api_protect_course_script(true);
14
15
$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
16
$debug = false;
17
$origin = api_get_origin();
18
if (!$is_allowed_to_edit) {
19
    api_not_allowed(true);
20
}
21
22
$this_section = SECTION_COURSES;
23
$htmlHeadXtra[] = "<script>
24
$(function(){
25
    $('#user_custom_score').click(function() {
26
        $('#options').toggle();
27
    });
28
});
29
</script>";
30
31
// Action handling
32
lp_upload_quiz_action_handling();
33
34
$interbreadcrumb[] = [
35
    'url' => 'exercise.php?'.api_get_cidreq(),
36
    'name' => get_lang('Tests'),
37
];
38
39
Display::display_header(get_lang('Import quiz from Excel'), 'Exercises');
40
41
echo '<div class="actions">';
42
echo lp_upload_quiz_actions();
43
echo '</div>';
44
45
// the main content
46
lp_upload_quiz_main();
47
48
function lp_upload_quiz_actions()
49
{
50
    return '<a href="exercise.php?'.api_get_cidreq().'">'.
51
        Display::return_icon(
52
            'back.png',
53
            get_lang('BackToTestsList'),
54
            '',
55
            ICON_SIZE_MEDIUM
56
        ).'</a>';
57
}
58
59
function lp_upload_quiz_main()
60
{
61
    $lp_id = isset($_GET['lp_id']) ? (int) $_GET['lp_id'] : null;
62
63
    $form = new FormValidator(
64
        'upload',
65
        'POST',
66
        api_get_self().'?'.api_get_cidreq().'&lp_id='.$lp_id,
67
        '',
68
        ['enctype' => 'multipart/form-data']
69
    );
70
    $form->addElement('header', get_lang('Import quiz from Excel'));
71
    $form->addElement('file', 'user_upload_quiz', get_lang('File upload'));
72
73
    $link = '<a href="../exercise/quiz_template.xls">'.
74
        Display::return_icon('export_excel.png', get_lang('Download the Excel Template')).get_lang('Download the Excel Template').'</a>';
75
    $form->addElement('label', '', $link);
76
77
    $table = new HTML_Table(['class' => 'table']);
78
79
    $tableList = [
80
        UNIQUE_ANSWER => get_lang('Multiple choice'),
81
        MULTIPLE_ANSWER => get_lang('Multiple answers'),
82
        FILL_IN_BLANKS => get_lang('Fill blanks or form'),
83
        MATCHING => get_lang('Matching'),
84
        FREE_ANSWER => get_lang('Open question'),
85
        GLOBAL_MULTIPLE_ANSWER => get_lang('Global multiple answer'),
86
    ];
87
88
    $table->setHeaderContents(0, 0, get_lang('Question type'));
89
    $table->setHeaderContents(0, 1, '#');
90
91
    $row = 1;
92
    foreach ($tableList as $key => $label) {
93
        $table->setCellContents($row, 0, $label);
94
        $table->setCellContents($row, 1, $key);
95
        $row++;
96
    }
97
    $table = $table->toHtml();
98
99
    $form->addElement('label', get_lang('Question type'), $table);
100
    $form->addElement(
101
        'checkbox',
102
        'user_custom_score',
103
        null,
104
        get_lang('Use custom score for all questions'),
105
        ['id' => 'user_custom_score']
106
    );
107
    $form->addElement('html', '<div id="options" style="display:none">');
108
    $form->addElement('text', 'correct_score', get_lang('Correct score'));
109
    $form->addElement('text', 'incorrect_score', get_lang('Incorrect score'));
110
    $form->addElement('html', '</div>');
111
112
    $form->addRule('user_upload_quiz', get_lang('Required field'), 'required');
113
114
    $form->addProgress();
115
    $form->addButtonUpload(get_lang('Upload'), 'submit_upload_quiz');
116
    $form->display();
117
}
118
119
/**
120
 * Handles a given Excel spreadsheets as in the template provided.
121
 */
122
function lp_upload_quiz_action_handling()
123
{
124
    if (!isset($_POST['submit_upload_quiz'])) {
125
        return;
126
    }
127
128
    $_course = api_get_course_info();
129
130
    if (empty($_course)) {
131
        return false;
132
    }
133
134
    $courseId = $_course['real_id'];
135
136
    // Get the extension of the document.
137
    $path_info = pathinfo($_FILES['user_upload_quiz']['name']);
138
139
    // Check if the document is an Excel document
140
    if (!in_array($path_info['extension'], ['xls', 'xlsx'])) {
141
        return;
142
    }
143
144
    // Variables
145
    $numberQuestions = 0;
146
    $question = [];
147
    $scoreList = [];
148
    $feedbackTrueList = [];
149
    $feedbackFalseList = [];
150
    $questionDescriptionList = [];
151
    $noNegativeScoreList = [];
152
    $questionTypeList = [];
153
    $answerList = [];
154
    $quizTitle = '';
155
156
    $objPHPExcel = PHPExcel_IOFactory::load($_FILES['user_upload_quiz']['tmp_name']);
157
    $objPHPExcel->setActiveSheetIndex(0);
158
    $worksheet = $objPHPExcel->getActiveSheet();
159
    $highestRow = $worksheet->getHighestRow(); // e.g. 10
160
    //  $highestColumn = $worksheet->getHighestColumn(); // e.g 'F'
161
162
    $correctScore = isset($_POST['correct_score']) ? $_POST['correct_score'] : null;
163
    $incorrectScore = isset($_POST['incorrect_score']) ? $_POST['incorrect_score'] : null;
164
    $useCustomScore = isset($_POST['user_custom_score']) ? true : false;
165
166
    for ($row = 1; $row <= $highestRow; $row++) {
167
        $cellTitleInfo = $worksheet->getCellByColumnAndRow(0, $row);
168
        $cellDataInfo = $worksheet->getCellByColumnAndRow(1, $row);
169
        $cellScoreInfo = $worksheet->getCellByColumnAndRow(2, $row);
170
        $title = $cellTitleInfo->getValue();
171
172
        switch ($title) {
173
            case 'Quiz':
174
                $quizTitle = $cellDataInfo->getValue();
175
176
                break;
177
            case 'Question':
178
                $question[] = $cellDataInfo->getValue();
179
                // Search cell with Answer title
180
                $answerRow = $row;
181
                $continue = true;
182
                $answerIndex = 0;
183
                while ($continue) {
184
                    $answerRow++;
185
                    $answerInfoTitle = $worksheet->getCellByColumnAndRow(0, $answerRow);
186
                    $answerInfoData = $worksheet->getCellByColumnAndRow(1, $answerRow);
187
                    $answerInfoExtra = $worksheet->getCellByColumnAndRow(2, $answerRow);
188
                    $answerInfoTitle = $answerInfoTitle->getValue();
189
                    if (false !== strpos($answerInfoTitle, 'Answer')) {
190
                        $answerList[$numberQuestions][$answerIndex]['data'] = $answerInfoData->getValue();
191
                        $answerList[$numberQuestions][$answerIndex]['extra'] = $answerInfoExtra->getValue();
192
                    } else {
193
                        $continue = false;
194
                    }
195
                    $answerIndex++;
196
197
                    // To avoid loops
198
                    if ($answerIndex > 60) {
199
                        $continue = false;
200
                    }
201
                }
202
203
                // Search cell with question type
204
                $answerRow = $row;
205
                $continue = true;
206
                $questionTypeIndex = 0;
207
                while ($continue) {
208
                    $answerRow++;
209
                    $questionTypeTitle = $worksheet->getCellByColumnAndRow(0, $answerRow);
210
                    $questionTypeExtra = $worksheet->getCellByColumnAndRow(2, $answerRow);
211
                    $title = $questionTypeTitle->getValue();
212
                    if ('QuestionType' == $title) {
213
                        $questionTypeList[$numberQuestions] = $questionTypeExtra->getValue();
214
                        $continue = false;
215
                    }
216
                    if ('Question' == $title) {
217
                        $continue = false;
218
                    }
219
                    // To avoid loops
220
                    if ($questionTypeIndex > 60) {
221
                        $continue = false;
222
                    }
223
                    $questionTypeIndex++;
224
                }
225
226
                // Detect answers
227
                $numberQuestions++;
228
229
                break;
230
            case 'Score':
231
                $scoreList[] = $cellScoreInfo->getValue();
232
233
                break;
234
            case 'NoNegativeScore':
235
                $noNegativeScoreList[] = $cellScoreInfo->getValue();
236
237
                break;
238
            case 'Category':
239
                $categoryList[] = $cellDataInfo->getValue();
240
241
                break;
242
            case 'FeedbackTrue':
243
                $feedbackTrueList[] = $cellDataInfo->getValue();
244
245
                break;
246
            case 'FeedbackFalse':
247
                $feedbackFalseList[] = $cellDataInfo->getValue();
248
249
                break;
250
            case 'EnrichQuestion':
251
                $questionDescriptionList[] = $cellDataInfo->getValue();
252
253
                break;
254
        }
255
    }
256
257
    $propagateNegative = 0;
258
    if ($useCustomScore && !empty($incorrectScore)) {
259
        if ($incorrectScore < 0) {
260
            $propagateNegative = 1;
261
        }
262
    }
263
264
    $url = api_get_path(WEB_CODE_PATH).'exercise/upload_exercise.php?'.api_get_cidreq();
265
266
    if (empty($quizTitle)) {
267
        Display::addFlash(Display::return_message('ErrorImportingFile'), 'warning');
268
        api_location($url);
269
    }
270
271
    // Variables
272
    $type = 2;
273
    $random = $active = $results = $max_attempt = $expired_time = 0;
274
    // Make sure feedback is enabled (3 to disable), otherwise the fields
275
    // added to the XLS are not shown, which is confusing
276
    $feedback = 0;
277
278
    // Quiz object
279
    $exercise = new Exercise();
280
    $exercise->updateTitle($quizTitle);
281
    $exercise->updateExpiredTime($expired_time);
282
    $exercise->updateType($type);
283
    $exercise->setRandom($random);
284
    $exercise->active = $active;
285
    $exercise->updateResultsDisabled($results);
286
    $exercise->updateAttempts($max_attempt);
287
    $exercise->updateFeedbackType($feedback);
288
    $exercise->updatePropagateNegative($propagateNegative);
289
    $quiz_id = $exercise->save();
290
291
    if ($quiz_id) {
292
        // Import questions.
293
        for ($i = 0; $i < $numberQuestions; $i++) {
294
            // Question name
295
            $questionTitle = $question[$i];
296
            $myAnswerList = isset($answerList[$i]) ? $answerList[$i] : [];
297
            $description = isset($questionDescriptionList[$i]) ? $questionDescriptionList[$i] : '';
298
            $categoryId = null;
299
            if (isset($categoryList[$i]) && !empty($categoryList[$i])) {
300
                $categoryName = $categoryList[$i];
301
                $categoryId = TestCategory::get_category_id_for_title($categoryName, $courseId);
302
                if (empty($categoryId)) {
303
                    $category = new TestCategory();
304
                    $category->name = $categoryName;
305
                    $categoryId = $category->save();
306
                }
307
            }
308
309
            $question_description_text = '<p></p>';
310
            if (!empty($description)) {
311
                // Question description.
312
                $question_description_text = "<p>$description</p>";
313
            }
314
315
            // Unique answers are the only question types available for now
316
            // through xls-format import
317
            $question_id = null;
318
            if (isset($questionTypeList[$i]) && '' != $questionTypeList[$i]) {
319
                $detectQuestionType = (int) $questionTypeList[$i];
320
            } else {
321
                $detectQuestionType = detectQuestionType($myAnswerList);
322
            }
323
324
            /** @var Question $answer */
325
            switch ($detectQuestionType) {
326
                case FREE_ANSWER:
327
                    $answer = new FreeAnswer();
328
329
                    break;
330
                case GLOBAL_MULTIPLE_ANSWER:
331
                    $answer = new GlobalMultipleAnswer();
332
333
                    break;
334
                case MULTIPLE_ANSWER:
335
                    $answer = new MultipleAnswer();
336
337
                    break;
338
                case FILL_IN_BLANKS:
339
                    $answer = new FillBlanks();
340
                    $question_description_text = '';
341
342
                    break;
343
                case MATCHING:
344
                    $answer = new Matching();
345
346
                    break;
347
                case UNIQUE_ANSWER:
348
                default:
349
                    $answer = new UniqueAnswer();
350
351
                    break;
352
            }
353
354
            if ('' != $questionTitle) {
355
                $question_id = $answer->create_question(
0 ignored issues
show
Deprecated Code introduced by
The function Question::create_question() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

355
                $question_id = /** @scrutinizer ignore-deprecated */ $answer->create_question(
Loading history...
356
                    $quiz_id,
357
                    $questionTitle,
358
                    $question_description_text,
359
                    0, // max score
360
                    $answer->type
361
                );
362
363
                if (!empty($categoryId)) {
364
                    TestCategory::addCategoryToQuestion(
365
                        $categoryId,
366
                        $question_id,
367
                        $courseId
368
                    );
369
                }
370
            }
371
            switch ($detectQuestionType) {
372
                case GLOBAL_MULTIPLE_ANSWER:
373
                case MULTIPLE_ANSWER:
374
                case UNIQUE_ANSWER:
375
                    $total = 0;
376
                    if (is_array($myAnswerList) && !empty($myAnswerList) && !empty($question_id)) {
377
                        $id = 1;
378
                        $objAnswer = new Answer($question_id, $courseId);
379
                        $globalScore = isset($scoreList[$i]) ? $scoreList[$i] : null;
380
381
                        // Calculate the number of correct answers to divide the
382
                        // score between them when importing from CSV
383
                        $numberRightAnswers = 0;
384
                        foreach ($myAnswerList as $answer_data) {
385
                            if ('x' == strtolower($answer_data['extra'])) {
386
                                $numberRightAnswers++;
387
                            }
388
                        }
389
390
                        foreach ($myAnswerList as $answer_data) {
391
                            $answerValue = $answer_data['data'];
392
                            $correct = 0;
393
                            $score = 0;
394
                            if ('x' == strtolower($answer_data['extra'])) {
395
                                $correct = 1;
396
                                $score = isset($scoreList[$i]) ? $scoreList[$i] : 0;
397
                                $comment = isset($feedbackTrueList[$i]) ? $feedbackTrueList[$i] : '';
398
                            } else {
399
                                $comment = isset($feedbackFalseList[$i]) ? $feedbackFalseList[$i] : '';
400
                                $floatVal = (float) $answer_data['extra'];
401
                                if (is_numeric($floatVal)) {
402
                                    $score = $answer_data['extra'];
403
                                }
404
                            }
405
406
                            if ($useCustomScore) {
407
                                if ($correct) {
408
                                    $score = $correctScore;
409
                                } else {
410
                                    $score = $incorrectScore;
411
                                }
412
                            }
413
414
                            // Fixing scores:
415
                            switch ($detectQuestionType) {
416
                                case GLOBAL_MULTIPLE_ANSWER:
417
                                    if ($correct) {
418
                                        $score = abs($scoreList[$i]);
419
                                    } else {
420
                                        if (isset($noNegativeScoreList[$i]) && 'x' == $noNegativeScoreList[$i]) {
421
                                            $score = 0;
422
                                        } else {
423
                                            $score = -abs($scoreList[$i]);
424
                                        }
425
                                    }
426
                                    $score /= $numberRightAnswers;
427
428
                                    break;
429
                                case UNIQUE_ANSWER:
430
                                    break;
431
                                case MULTIPLE_ANSWER:
432
                                    if (!$correct) {
433
                                        //$total = $total - $score;
434
                                    }
435
436
                                    break;
437
                            }
438
439
                            $objAnswer->createAnswer(
440
                                $answerValue,
441
                                $correct,
442
                                $comment,
443
                                $score,
444
                                $id
445
                            );
446
                            if ($correct) {
447
                                $total += (float) $score;
448
                            }
449
                            $id++;
450
                        }
451
452
                        $objAnswer->save();
453
454
                        $questionObj = Question::read(
455
                            $question_id,
456
                            $_course
457
                        );
458
459
                        if ($questionObj) {
460
                            switch ($detectQuestionType) {
461
                                case GLOBAL_MULTIPLE_ANSWER:
462
                                    $questionObj->updateWeighting($globalScore);
463
464
                                    break;
465
                                case UNIQUE_ANSWER:
466
                                case MULTIPLE_ANSWER:
467
                                default:
468
                                    $questionObj->updateWeighting($total);
469
470
                                    break;
471
                            }
472
                            $questionObj->save($exercise);
473
                        }
474
                    }
475
476
                    break;
477
                case FREE_ANSWER:
478
                    $globalScore = isset($scoreList[$i]) ? $scoreList[$i] : null;
479
                    $questionObj = Question::read($question_id, $_course);
480
                    if ($questionObj) {
481
                        $questionObj->updateWeighting($globalScore);
482
                        $questionObj->save($exercise);
483
                    }
484
485
                    break;
486
                case FILL_IN_BLANKS:
487
                    $fillInScoreList = [];
488
                    $size = [];
489
                    $globalScore = 0;
490
                    foreach ($myAnswerList as $data) {
491
                        $score = isset($data['extra']) ? $data['extra'] : 0;
492
                        $globalScore += $score;
493
                        $fillInScoreList[] = $score;
494
                        $size[] = 200;
495
                    }
496
497
                    $scoreToString = implode(',', $fillInScoreList);
498
                    $sizeToString = implode(',', $size);
499
500
                    //<p>Texte long avec les [mots] à [remplir] mis entre [crochets]</p>::10,10,10:200.36363999999998,200,200:0@'
501
                    $answerValue = $description.'::'.$scoreToString.':'.$sizeToString.':0@';
502
                    $objAnswer = new Answer($question_id, $courseId);
503
                    $objAnswer->createAnswer(
504
                        $answerValue,
505
                        '', //$correct,
506
                        '', //$comment,
507
                        $globalScore,
508
                        1
509
                    );
510
511
                    $objAnswer->save();
512
513
                    $questionObj = Question::read($question_id, $_course);
514
                    if ($questionObj) {
515
                        $questionObj->updateWeighting($globalScore);
516
                        $questionObj->save($exercise);
517
                    }
518
519
                    break;
520
                case MATCHING:
521
                    $globalScore = isset($scoreList[$i]) ? $scoreList[$i] : null;
522
                    $position = 1;
523
524
                    $objAnswer = new Answer($question_id, $courseId);
525
                    foreach ($myAnswerList as $data) {
526
                        $option = isset($data['extra']) ? $data['extra'] : '';
527
                        $objAnswer->createAnswer($option, 0, '', 0, $position);
528
                        $position++;
529
                    }
530
531
                    $counter = 1;
532
                    foreach ($myAnswerList as $data) {
533
                        $value = isset($data['data']) ? $data['data'] : '';
534
                        $position++;
535
                        $objAnswer->createAnswer(
536
                            $value,
537
                            $counter,
538
                            ' ',
539
                            $globalScore,
540
                            $position
541
                        );
542
                        $counter++;
543
                    }
544
                    $objAnswer->save();
545
                    $questionObj = Question::read($question_id, $_course);
546
                    if ($questionObj) {
547
                        $questionObj->updateWeighting($globalScore);
548
                        $questionObj->save($exercise);
549
                    }
550
551
                    break;
552
            }
553
        }
554
    }
555
556
    $lpObject = Session::read('lpobject');
557
558
    if (!empty($lpObject)) {
559
        /** @var learnpath $oLP */
560
        $oLP = UnserializeApi::unserialize('lp', $lpObject);
561
        if (is_object($oLP)) {
562
            if (empty($oLP->cc) || $oLP->cc != api_get_course_id()) {
563
                $oLP = null;
564
                Session::erase('oLP');
565
                Session::erase('lpobject');
566
            } else {
567
                Session::write('oLP', $oLP);
568
            }
569
        }
570
    }
571
    Display::addFlash(Display::return_message(get_lang('FileImported')));
572
573
    if (isset($_SESSION['oLP']) && isset($_GET['lp_id'])) {
574
        $previous = $_SESSION['oLP']->select_previous_item_id();
575
        $parent = 0;
576
        // Add a Quiz as Lp Item
577
        $_SESSION['oLP']->add_item(
578
            $parent,
579
            $previous,
580
            TOOL_QUIZ,
581
            $quiz_id,
582
            $quizTitle,
583
            ''
584
        );
585
        // Redirect to home page for add more content
586
        header('Location: ../lp/lp_controller.php?'.api_get_cidreq().'&action=add_item&type=step&lp_id='.(int) ($_GET['lp_id']));
587
        exit;
588
    } else {
589
        //  header('location: exercise.php?' . api_get_cidreq());
590
        $exerciseUrl = api_get_path(WEB_CODE_PATH).
591
            'exercise/admin.php?'.api_get_cidreq().'&exerciseId='.$quiz_id.'&session_id='.api_get_session_id();
592
        api_location($exerciseUrl);
593
    }
594
}
595
596
/**
597
 * @param array $answers_data
598
 *
599
 * @return int
600
 */
601
function detectQuestionType($answers_data)
602
{
603
    $correct = 0;
604
    $isNumeric = false;
605
606
    if (empty($answers_data)) {
607
        return FREE_ANSWER;
608
    }
609
610
    foreach ($answers_data as $answer_data) {
611
        if ('x' == strtolower($answer_data['extra'])) {
612
            $correct++;
613
        } else {
614
            if (is_numeric($answer_data['extra'])) {
615
                $isNumeric = true;
616
            }
617
        }
618
    }
619
620
    if (1 == $correct) {
621
        $type = UNIQUE_ANSWER;
622
    } else {
623
        if ($correct > 1) {
624
            $type = MULTIPLE_ANSWER;
625
        } else {
626
            $type = FREE_ANSWER;
627
        }
628
    }
629
630
    if (MULTIPLE_ANSWER == $type) {
631
        if ($isNumeric) {
632
            $type = MULTIPLE_ANSWER;
633
        } else {
634
            $type = GLOBAL_MULTIPLE_ANSWER;
635
        }
636
    }
637
638
    return $type;
639
}
640
641
if ('learnpath' != $origin) {
642
    //so we are not in learnpath tool
643
    Display::display_footer();
644
}
645