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 * Guesses course start and end dates based on activity logs.
19 *
20 * @package    tool_analytics
21 * @copyright  2016 David Monllao {@link http://www.davidmonllao.com}
22 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25define('CLI_SCRIPT', true);
26
27require_once(__DIR__ . '/../../../../config.php');
28require_once($CFG->libdir.'/clilib.php');
29
30require_once($CFG->dirroot . '/course/lib.php');
31require_once($CFG->dirroot . '/course/format/weeks/lib.php');
32
33$help = "Guesses course start and end dates based on activity logs.
34
35IMPORTANT: Don't use this script if you keep previous academic years users enrolled in courses. Guesses would not be accurate.
36
37Options:
38--guessstart           Guess the course start date (default to true)
39--guessend             Guess the course end date (default to true)
40--guessall             Guess all start and end dates, even if they are already set (default to false)
41--update               Update the db or just notify the guess (default to false)
42--filter               Analyser dependant. e.g. A courseid would evaluate the model using a single course (Optional)
43-h, --help             Print out this help
44
45Example:
46\$ php admin/tool/analytics/cli/guess_course_start_and_end_dates.php --update=1 --filter=123,321
47";
48
49// Now get cli options.
50list($options, $unrecognized) = cli_get_params(
51    array(
52        'help'        => false,
53        'guessstart'  => true,
54        'guessend'    => true,
55        'guessall'    => false,
56        'update'      => false,
57        'filter'      => false
58    ),
59    array(
60        'h' => 'help',
61    )
62);
63
64if ($options['help']) {
65    echo $help;
66    exit(0);
67}
68
69if ($options['guessstart'] === false && $options['guessend'] === false && $options['guessall'] === false) {
70    echo $help;
71    exit(0);
72}
73
74// Reformat them as an array.
75if ($options['filter'] !== false) {
76    $options['filter'] = explode(',', clean_param($options['filter'], PARAM_SEQUENCE));
77}
78
79// We need admin permissions.
80\core\session\manager::set_user(get_admin());
81
82$conditions = array('id != 1');
83if (!$options['guessall']) {
84    if ($options['guessstart']) {
85        $conditions[] = '(startdate is null or startdate = 0)';
86    }
87    if ($options['guessend']) {
88        $conditions[] = '(enddate is null or enddate = 0)';
89    }
90}
91
92$coursessql = '';
93$params = null;
94if ($options['filter']) {
95    list($coursessql, $params) = $DB->get_in_or_equal($options['filter'], SQL_PARAMS_NAMED);
96    $conditions[] = 'id ' . $coursessql;
97}
98
99$courses = $DB->get_recordset_select('course', implode(' AND ', $conditions), $params, 'sortorder ASC');
100foreach ($courses as $course) {
101    tool_analytics_calculate_course_dates($course, $options);
102}
103$courses->close();
104
105
106/**
107 * tool_analytics_calculate_course_dates
108 *
109 * @param stdClass $course
110 * @param array $options CLI options
111 * @return void
112 */
113function tool_analytics_calculate_course_dates($course, $options) {
114    global $DB, $OUTPUT;
115
116    $courseman = new \core_analytics\course($course);
117
118    $notification = $course->shortname . ' (id = ' . $course->id . '): ';
119
120    $originalenddate = null;
121    $guessedstartdate = null;
122    $guessedenddate = null;
123    $samestartdate = null;
124    $lowerenddate = null;
125
126    if ($options['guessstart'] || $options['guessall']) {
127
128        $originalstartdate = $course->startdate;
129
130        $guessedstartdate = $courseman->guess_start();
131        $samestartdate = ($guessedstartdate == $originalstartdate);
132        $lowerenddate = ($course->enddate && ($course->enddate < $guessedstartdate));
133
134        if ($samestartdate) {
135            if (!$guessedstartdate) {
136                $notification .= PHP_EOL . '  ' . get_string('cantguessstartdate', 'tool_analytics');
137            } else {
138                // No need to update.
139                $notification .= PHP_EOL . '  ' . get_string('samestartdate', 'tool_analytics') . ': ' . userdate($guessedstartdate);
140            }
141        } else if (!$guessedstartdate) {
142            $notification .= PHP_EOL . '  ' . get_string('cantguessstartdate', 'tool_analytics');
143        } else if ($lowerenddate) {
144            $notification .= PHP_EOL . '  ' . get_string('cantguessstartdate', 'tool_analytics') . ': ' .
145                get_string('enddatebeforestartdate', 'error') . ' - ' . userdate($guessedstartdate);
146        } else {
147            // Update it to something we guess.
148
149            // We set it to $course even if we don't update because may be needed to guess the end one.
150            $course->startdate = $guessedstartdate;
151            $notification .= PHP_EOL . '  ' . get_string('startdate') . ': ' . userdate($guessedstartdate);
152
153            // Two different course updates because week's end date may be recalculated after setting the start date.
154            if ($options['update']) {
155                update_course($course);
156
157                // Refresh course data as end date may have been updated.
158                $course = $DB->get_record('course', array('id' => $course->id));
159                $courseman = new \core_analytics\course($course);
160            }
161        }
162    }
163
164    if ($options['guessend'] || $options['guessall']) {
165
166        if (!empty($lowerenddate) && !empty($guessedstartdate)) {
167            $course->startdate = $guessedstartdate;
168        }
169
170        $originalenddate = $course->enddate;
171
172        $format = course_get_format($course);
173        $formatoptions = $format->get_format_options();
174
175        // Change this for a course formats API level call in MDL-60702.
176        if ((get_class($format) == 'format_weeks' || is_subclass_of($format, 'format_weeks')) &&
177                method_exists($format, 'update_end_date') && $formatoptions['automaticenddate']) {
178            // Special treatment for weeks-based formats with automatic end date.
179
180            if ($options['update']) {
181                $format::update_end_date($course->id);
182                $course->enddate = $DB->get_field('course', 'enddate', array('id' => $course->id));
183                $notification .= PHP_EOL . '  ' . get_string('weeksenddateautomaticallyset', 'tool_analytics') . ': ' .
184                    userdate($course->enddate);
185            } else {
186                // We can't provide more info without actually updating it in db.
187                $notification .= PHP_EOL . '  ' . get_string('weeksenddatedefault', 'tool_analytics');
188            }
189        } else {
190            $guessedenddate = $courseman->guess_end();
191
192            if ($guessedenddate == $originalenddate) {
193                if (!$guessedenddate) {
194                    $notification .= PHP_EOL . '  ' . get_string('cantguessenddate', 'tool_analytics');
195                } else {
196                    // No need to update.
197                    $notification .= PHP_EOL . '  ' . get_string('sameenddate', 'tool_analytics') . ': ' . userdate($guessedenddate);
198                }
199            } else if (!$guessedenddate) {
200                $notification .= PHP_EOL . '  ' . get_string('cantguessenddate', 'tool_analytics');
201            } else {
202                // Update it to something we guess.
203
204                $course->enddate = $guessedenddate;
205
206                $updateit = false;
207                if ($course->enddate < $course->startdate) {
208                    $notification .= PHP_EOL . '  ' . get_string('errorendbeforestart', 'course', userdate($course->enddate));
209                } else if ($course->startdate + (YEARSECS + (WEEKSECS * 4)) > $course->enddate) {
210                    $notification .= PHP_EOL . '  ' . get_string('coursetoolong', 'course');
211                } else {
212                    $notification .= PHP_EOL . '  ' . get_string('enddate') . ': ' . userdate($course->enddate);
213                    $updateit = true;
214                }
215
216                if ($options['update'] && $updateit) {
217                    update_course($course);
218                }
219            }
220        }
221
222    }
223
224    mtrace($notification);
225}
226
227mtrace(get_string('success'));
228
229exit(0);
230