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
namespace tool_courserating\local\models;
18
 
19
use tool_courserating\constants;
20
use tool_courserating\helper;
21
 
22
/**
23
 * Model for summary table
24
 *
25
 * @package     tool_courserating
26
 * @copyright   2022 Marina Glancy <marina.glancy@gmail.com>
27
 * @license     https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28
 */
29
class summary extends \core\persistent {
30
 
31
    /** @var string Table name */
32
    public const TABLE = 'tool_courserating_summary';
33
 
34
    /**
35
     * Return the definition of the properties of this model.
36
     *
37
     * @return array
38
     */
39
    protected static function define_properties(): array {
40
        $props = [
41
            'courseid' => [
42
                'type' => PARAM_INT,
43
            ],
44
            'cntall' => [
45
                'type' => PARAM_INT,
46
                'default' => 0,
47
            ],
48
            'avgrating' => [
49
                'type' => PARAM_FLOAT,
50
                'null' => NULL_ALLOWED,
51
                'default' => null,
52
            ],
53
            'sumrating' => [
54
                'type' => PARAM_INT,
55
                'default' => 0,
56
            ],
57
            'cntreviews' => [
58
                'type' => PARAM_INT,
59
                'default' => 0,
60
            ],
61
            'ratingmode' => [
62
                'type' => PARAM_INT,
63
                'default' => 0,
64
            ],
65
        ];
66
        for ($i = 1; $i <= 10; $i++) {
67
            $props[self::cntkey($i)] = [
68
                'type' => PARAM_INT,
69
                'default' => 0,
70
            ];
71
        }
72
        return $props;
73
    }
74
 
75
    /**
76
     * Retrieve summary for the specified course, insert in DB if does not exist
77
     *
78
     * @param int $courseid
79
     * @return summary
80
     */
81
    public static function get_for_course(int $courseid): summary {
82
        if ($summary = self::get_record(['courseid' => $courseid])) {
83
            return $summary;
84
        } else {
85
            $summary = new static(0, (object)[
86
                'courseid' => $courseid,
87
                'ratingmode' => helper::get_course_rating_mode($courseid),
88
            ]);
89
            $summary->save();
90
            return $summary;
91
        }
92
    }
93
 
94
    /**
95
     * Field name for the counter of rating $i
96
     *
97
     * @param int $i
98
     * @return string
99
     */
100
    protected static function cntkey(int $i) {
101
        $i = min(max(1, $i), 10);
102
        return 'cnt' . str_pad($i, 2, "0", STR_PAD_LEFT);
103
    }
104
 
105
    /**
106
     * Update summary after a rating was added
107
     *
108
     * @param int $courseid
109
     * @param rating $rating
110
     * @return static
111
     */
112
    public static function add_rating(int $courseid, rating $rating): self {
113
        if (!$record = self::get_record(['courseid' => $courseid])) {
114
            $record = new self(0, (object)['courseid' => $courseid]);
115
        }
116
        $record->set('cntall', $record->get('cntall') + 1);
117
        $record->set('sumrating', $record->get('sumrating') + $rating->get('rating'));
118
        $record->set('avgrating', 1.0 * $record->get('sumrating') / $record->get('cntall'));
119
        if ($rating->get('hasreview')) {
120
            $record->set('cntreviews', $record->get('cntreviews') + 1);
121
        }
122
        $record->set(self::cntkey($rating->get('rating')), $record->get(self::cntkey($rating->get('rating'))) + 1);
123
        $record->save();
124
        return $record;
125
    }
126
 
127
    /**
128
     * Update summary after a rating was updated
129
     *
130
     * @param int $courseid
131
     * @param rating $rating
132
     * @param \stdClass $oldrecord
133
     * @return static|null
134
     */
135
    public static function update_rating(int $courseid, rating $rating, \stdClass $oldrecord): ?self {
136
        $ratingold = $oldrecord->rating;
137
        $summary = self::get_for_course($courseid);
138
        if (!$summary->get('cntall') || !$summary->get(self::cntkey($ratingold))) {
139
            // Sanity check, did not pass, recalculate all.
140
            return $summary->recalculate();
141
        }
142
        if ($rating == $ratingold && $rating->get('hasreview') == $oldrecord->hasreview) {
143
            // Rating did not change.
144
            return null;
145
        }
146
        $summary->set('cntreviews', $summary->get('cntreviews') + $rating->get('hasreview') - $oldrecord->hasreview);
147
        if ($rating != $ratingold) {
148
            $summary->set('sumrating', $summary->get('sumrating') + $rating->get('rating') - $ratingold);
149
            $summary->set(self::cntkey($ratingold), $summary->get(self::cntkey($ratingold)) - 1);
150
            $summary->set(self::cntkey($rating->get('rating')), (int)$summary->get(self::cntkey($rating->get('rating'))) + 1);
151
            $summary->set('avgrating', 1.0 * $summary->get('sumrating') / $summary->get('cntall'));
152
        }
153
        $summary->save();
154
        return $summary;
155
    }
156
 
157
    /**
158
     * Update summary when it has to be empty - reset all counter fields
159
     *
160
     * @return void
161
     * @throws \coding_exception
162
     */
163
    public function reset_all_counters() {
164
        foreach (['cntall', 'sumrating', 'cntreviews'] as $key) {
165
            $this->set($key, 0);
166
        }
167
        $this->set('avgrating', null);
168
        for ($i = 1; $i <= 10; $i++) {
169
            $this->set(self::cntkey($i), 0);
170
        }
171
    }
172
 
173
    /**
174
     * Recalculate summary for the specific course
175
     *
176
     * @return $this|null
177
     */
178
    public function recalculate(): ?self {
179
        global $DB;
180
        if ($this->get('ratingmode') == constants::RATEBY_NOONE) {
181
            $this->reset_all_counters();
182
            $this->save();
183
            return $this;
184
        }
185
 
186
        $isempty = $DB->sql_isempty('', 'review', false, true);
187
        $sql = 'SELECT COUNT(id) AS cntall,
188
               SUM(rating) AS sumrating,
189
               SUM(CASE WHEN '.$isempty.' THEN 0 ELSE 1 END) as cntreviews,
190
               SUM(CASE WHEN rating = 1 THEN 1 ELSE 0 END) as cnt01,
191
               SUM(CASE WHEN rating = 2 THEN 1 ELSE 0 END) as cnt02,
192
               SUM(CASE WHEN rating = 3 THEN 1 ELSE 0 END) as cnt03,
193
               SUM(CASE WHEN rating = 4 THEN 1 ELSE 0 END) as cnt04,
194
               SUM(CASE WHEN rating = 5 THEN 1 ELSE 0 END) as cnt05,
195
               SUM(CASE WHEN rating = 6 THEN 1 ELSE 0 END) as cnt06,
196
               SUM(CASE WHEN rating = 7 THEN 1 ELSE 0 END) as cnt07,
197
               SUM(CASE WHEN rating = 8 THEN 1 ELSE 0 END) as cnt08,
198
               SUM(CASE WHEN rating = 9 THEN 1 ELSE 0 END) as cnt09,
199
               SUM(CASE WHEN rating = 10 THEN 1 ELSE 0 END) as cnt10
200
            FROM {tool_courserating_rating} r
201
            WHERE r.courseid = :courseid
202
        ';
203
        $params = ['courseid' => $this->get('courseid')];
204
        $result = $DB->get_record_sql($sql, $params);
205
 
206
        if (!$result->cntall) {
207
            $this->reset_all_counters();
208
        } else {
209
            $keys = ['cntall', 'sumrating', 'cntreviews'];
210
            for ($i = 1; $i <= 10; $i++) {
211
                $keys[] = self::cntkey($i);
212
            }
213
            foreach ($keys as $key) {
214
                $this->set($key, $result->$key ?? 0);
215
            }
216
            $this->set('avgrating', 1.0 * $this->get('sumrating') / $this->get('cntall'));
217
        }
218
        $this->save();
219
        return $this;
220
    }
221
 
222
    /**
223
     * Recalculate summary after an individual rating was deleted
224
     *
225
     * @param \stdClass $rating snapshot of the rating record before it was deleted
226
     * @return static|null
227
     */
228
    public static function delete_rating(\stdClass $rating): ?self {
229
        $ratingold = $rating->rating;
230
        $hasreviewold = $rating->hasreview;
231
        $record = self::get_for_course($rating->courseid);
232
        if ($record->get('cntall') <= 1 || !$record->get(self::cntkey($ratingold))) {
233
            // Sanity check did not pass or no more ratings left, recalculate all.
234
            return $record->recalculate();
235
        }
236
        if ($hasreviewold && $record->get('cntreviews') > 0) {
237
            $record->set('cntreviews', $record->get('cntreviews') - 1);
238
        }
239
        $record->set('cntall', $record->get('cntall') - 1);
240
        $record->set('sumrating', $record->get('sumrating') - $ratingold);
241
        $record->set(self::cntkey($ratingold), $record->get(self::cntkey($ratingold)) - 1);
242
        $record->set('avgrating', 1.0 * $record->get('sumrating') / $record->get('cntall'));
243
        $record->save();
244
        return $record;
245
    }
246
}