Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * File containing processor class.
19
 *
20
 * @package    tool_uploadcourse
21
 * @copyright  2013 Frédéric Massart
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
defined('MOODLE_INTERNAL') || die();
26
require_once($CFG->libdir . '/csvlib.class.php');
27
 
28
/**
29
 * Processor class.
30
 *
31
 * @package    tool_uploadcourse
32
 * @copyright  2013 Frédéric Massart
33
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 */
35
class tool_uploadcourse_processor {
36
 
37
    /**
38
     * Create courses that do not exist yet.
39
     */
40
    const MODE_CREATE_NEW = 1;
41
 
42
    /**
43
     * Create all courses, appending a suffix to the shortname if the course exists.
44
     */
45
    const MODE_CREATE_ALL = 2;
46
 
47
    /**
48
     * Create courses, and update the ones that already exist.
49
     */
50
    const MODE_CREATE_OR_UPDATE = 3;
51
 
52
    /**
53
     * Only update existing courses.
54
     */
55
    const MODE_UPDATE_ONLY = 4;
56
 
57
    /**
58
     * During update, do not update anything... O_o Huh?!
59
     */
60
    const UPDATE_NOTHING = 0;
61
 
62
    /**
63
     * During update, only use data passed from the CSV.
64
     */
65
    const UPDATE_ALL_WITH_DATA_ONLY = 1;
66
 
67
    /**
68
     * During update, use either data from the CSV, or defaults.
69
     */
70
    const UPDATE_ALL_WITH_DATA_OR_DEFAUTLS = 2;
71
 
72
    /**
73
     * During update, update missing values from either data from the CSV, or defaults.
74
     */
75
    const UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS = 3;
76
 
77
    /** @var int processor mode. */
78
    protected $mode;
79
 
80
    /** @var int upload mode. */
81
    protected $updatemode;
82
 
83
    /** @var bool are renames allowed. */
84
    protected $allowrenames = false;
85
 
86
    /** @var bool are deletes allowed. */
87
    protected $allowdeletes = false;
88
 
89
    /** @var bool are resets allowed. */
90
    protected $allowresets = false;
91
 
92
    /** @var string path to a restore file. */
93
    protected $restorefile;
94
 
95
    /** @var string shortname of the course to be restored. */
96
    protected $templatecourse;
97
 
98
    /** @var string reset courses after processing them. */
99
    protected $reset;
100
 
101
    /** @var string template to generate a course shortname. */
102
    protected $shortnametemplate;
103
 
104
    /** @var csv_import_reader */
105
    protected $cir;
106
 
107
    /** @var array default values. */
108
    protected $defaults = array();
109
 
110
    /** @var array CSV columns. */
111
    protected $columns = array();
112
 
113
    /** @var array of errors where the key is the line number. */
114
    protected $errors = array();
115
 
116
    /** @var int line number. */
117
    protected $linenb = 0;
118
 
119
    /** @var bool whether the process has been started or not. */
120
    protected $processstarted = false;
121
 
122
    /**
123
     * Constructor
124
     *
125
     * @param csv_import_reader $cir import reader object
126
     * @param array $options options of the process
127
     * @param array $defaults default data value
128
     */
129
    public function __construct(csv_import_reader $cir, array $options, array $defaults = array()) {
130
 
131
        if (!isset($options['mode']) || !in_array($options['mode'], array(self::MODE_CREATE_NEW, self::MODE_CREATE_ALL,
132
                self::MODE_CREATE_OR_UPDATE, self::MODE_UPDATE_ONLY))) {
133
            throw new coding_exception('Unknown process mode');
134
        }
135
 
136
        // Force int to make sure === comparison work as expected.
137
        $this->mode = (int) $options['mode'];
138
 
139
        $this->updatemode = self::UPDATE_NOTHING;
140
        if (isset($options['updatemode'])) {
141
            // Force int to make sure === comparison work as expected.
142
            $this->updatemode = (int) $options['updatemode'];
143
        }
144
        if (isset($options['allowrenames'])) {
145
            $this->allowrenames = $options['allowrenames'];
146
        }
147
        if (isset($options['allowdeletes'])) {
148
            $this->allowdeletes = $options['allowdeletes'];
149
        }
150
        if (isset($options['allowresets'])) {
151
            $this->allowresets = $options['allowresets'];
152
        }
153
 
154
        if (isset($options['restorefile'])) {
155
            $this->restorefile = $options['restorefile'];
156
        }
157
        if (isset($options['templatecourse'])) {
158
            $this->templatecourse = $options['templatecourse'];
159
        }
160
        if (isset($options['reset'])) {
161
            $this->reset = $options['reset'];
162
        }
163
        if (isset($options['shortnametemplate'])) {
164
            $this->shortnametemplate = $options['shortnametemplate'];
165
        }
166
 
167
        $this->cir = $cir;
168
        $this->columns = $cir->get_columns();
169
        $this->defaults = $defaults;
170
        $this->validate();
171
        $this->reset();
172
    }
173
 
174
    /**
175
     * Execute the process.
176
     *
177
     * @param tool_uploadcourse_tracker $tracker the output tracker to use.
178
     * @return void
179
     */
180
    public function execute($tracker = null) {
181
        if ($this->processstarted) {
182
            throw new coding_exception('Process has already been started');
183
        }
184
        $this->processstarted = true;
185
 
186
        if (empty($tracker)) {
187
            $tracker = new tool_uploadcourse_tracker(tool_uploadcourse_tracker::NO_OUTPUT);
188
        }
189
        $tracker->start();
190
 
191
        $total = 0;
192
        $created = 0;
193
        $updated = 0;
194
        $deleted = 0;
195
        $errors = 0;
196
 
197
        // We will most certainly need extra time and memory to process big files.
198
        core_php_time_limit::raise();
199
        raise_memory_limit(MEMORY_EXTRA);
200
 
201
        // Loop over the CSV lines.
202
        while ($line = $this->cir->next()) {
203
            $this->linenb++;
204
            $total++;
205
 
206
            $data = $this->parse_line($line);
207
            $course = $this->get_course($data);
208
            if ($course->prepare()) {
209
                $course->proceed();
210
 
211
                $status = $course->get_statuses();
212
                if (array_key_exists('coursecreated', $status)) {
213
                    $created++;
214
                } else if (array_key_exists('courseupdated', $status)) {
215
                    $updated++;
216
                } else if (array_key_exists('coursedeleted', $status)) {
217
                    $deleted++;
218
                }
219
 
220
                $data = array_merge($data, $course->get_data(), array('id' => $course->get_id()));
221
                $tracker->output($this->linenb, true, $status, $data);
222
                if ($course->has_errors()) {
223
                    $errors++;
224
                    $tracker->output($this->linenb, false, $course->get_errors(), $data);
225
                }
226
            } else {
227
                $errors++;
228
                $tracker->output($this->linenb, false, $course->get_errors(), $data);
229
            }
230
        }
231
 
232
        $tracker->finish();
233
        $tracker->results($total, $created, $updated, $deleted, $errors);
234
    }
235
 
236
    /**
237
     * Return a course import object.
238
     *
239
     * @param array $data data to import the course with.
240
     * @return tool_uploadcourse_course
241
     */
242
    protected function get_course($data) {
243
        $importoptions = array(
244
            'candelete' => $this->allowdeletes,
245
            'canrename' => $this->allowrenames,
246
            'canreset' => $this->allowresets,
247
            'reset' => $this->reset,
248
            'restoredir' => $this->get_restore_content_dir(),
249
            'shortnametemplate' => $this->shortnametemplate
250
        );
251
        return new tool_uploadcourse_course($this->mode, $this->updatemode, $data, $this->defaults, $importoptions);
252
    }
253
 
254
    /**
255
     * Return the errors.
256
     *
257
     * @return array
258
     */
259
    public function get_errors() {
260
        return $this->errors;
261
    }
262
 
263
    /**
264
     * Get the directory of the object to restore.
265
     *
266
     * @return string subdirectory in $CFG->backuptempdir/...
267
     */
268
    protected function get_restore_content_dir() {
269
        $backupfile = null;
270
        $shortname = null;
271
 
272
        if (!empty($this->restorefile)) {
273
            $backupfile = $this->restorefile;
274
        } else if (!empty($this->templatecourse) || is_numeric($this->templatecourse)) {
275
            $shortname = $this->templatecourse;
276
        }
277
 
278
        $dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname);
279
        return $dir;
280
    }
281
 
282
    /**
283
     * Log errors on the current line.
284
     *
285
     * @param array $errors array of errors
286
     * @return void
287
     */
288
    protected function log_error($errors) {
289
        if (empty($errors)) {
290
            return;
291
        }
292
 
293
        foreach ($errors as $code => $langstring) {
294
            if (!isset($this->errors[$this->linenb])) {
295
                $this->errors[$this->linenb] = array();
296
            }
297
            $this->errors[$this->linenb][$code] = $langstring;
298
        }
299
    }
300
 
301
    /**
302
     * Parse a line to return an array(column => value)
303
     *
304
     * @param array $line returned by csv_import_reader
305
     * @return array
306
     */
307
    protected function parse_line($line) {
308
        $data = array();
309
        foreach ($line as $keynum => $value) {
310
            if (!isset($this->columns[$keynum])) {
311
                // This should not happen.
312
                continue;
313
            }
314
 
315
            $key = $this->columns[$keynum];
316
            $data[$key] = $value;
317
        }
318
        return $data;
319
    }
320
 
321
    /**
322
     * Return a preview of the import.
323
     *
324
     * This only returns passed data, along with the errors.
325
     *
326
     * @param integer $rows number of rows to preview.
327
     * @param tool_uploadcourse_tracker $tracker the output tracker to use.
328
     * @return array of preview data.
329
     */
330
    public function preview($rows = 10, $tracker = null) {
331
        if ($this->processstarted) {
332
            throw new coding_exception('Process has already been started');
333
        }
334
        $this->processstarted = true;
335
 
336
        if (empty($tracker)) {
337
            $tracker = new tool_uploadcourse_tracker(tool_uploadcourse_tracker::NO_OUTPUT);
338
        }
339
        $tracker->start();
340
 
341
        // We might need extra time and memory depending on the number of rows to preview.
342
        core_php_time_limit::raise();
343
        raise_memory_limit(MEMORY_EXTRA);
344
 
345
        // Loop over the CSV lines.
346
        $preview = array();
347
        while (($line = $this->cir->next()) && $rows > $this->linenb) {
348
            $this->linenb++;
349
            $data = $this->parse_line($line);
350
            $course = $this->get_course($data);
351
            $result = $course->prepare();
352
            if (!$result) {
353
                $tracker->output($this->linenb, $result, $course->get_errors(), $data);
354
            } else {
355
                $tracker->output($this->linenb, $result, $course->get_statuses(), $data);
356
            }
357
            $row = $data;
358
            $preview[$this->linenb] = $row;
359
        }
360
 
361
        $tracker->finish();
362
 
363
        return $preview;
364
    }
365
 
366
    /**
367
     * Reset the current process.
368
     *
369
     * @return void.
370
     */
371
    public function reset() {
372
        $this->processstarted = false;
373
        $this->linenb = 0;
374
        $this->cir->init();
375
        $this->errors = array();
376
    }
377
 
378
    /**
379
     * Validation.
380
     *
381
     * @return void
382
     */
383
    protected function validate() {
384
        if (empty($this->columns)) {
385
            throw new moodle_exception('cannotreadtmpfile', 'error');
386
        } else if (count($this->columns) < 2) {
387
            throw new moodle_exception('csvfewcolumns', 'error');
388
        }
389
    }
390
}