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
 * Extra information generated during the analysis by calculable elements.
19
 *
20
 * @package   core_analytics
21
 * @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace core_analytics;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
/**
30
 * Extra information generated during the analysis by calculable elements.
31
 *
32
 * The main purpose of this request cache is to allow calculable elements to
33
 * store data during their calculations for further use at a later stage efficiently.
34
 *
35
 * @package   core_analytics
36
 * @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
37
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
39
class calculation_info {
40
 
41
    /**
42
     * @var array
43
     */
44
    private $info = [];
45
 
46
    /**
47
     * @var mixed[]
48
     */
49
    private $samplesinfo = [];
50
 
51
    /**
52
     * Adds info related to the current calculation for later use when generating insights.
53
     *
54
     * Note that the data in $info array is reused across multiple samples, if you want to add data just for this
55
     * sample you can use the sample id as key.
56
     *
57
     * We store two different arrays so objects that appear multiple times for different samples
58
     * appear just once in memory.
59
     *
60
     * @param int    $sampleid  The sample id this data is associated with
61
     * @param array  $info      The data. Indexed by an id unique across the site. E.g. an activity id.
62
     * @return null
63
     */
64
    public function add_shared(int $sampleid, array $info) {
65
 
66
        // We can safely overwrite the existing keys because the provided info is supposed to be unique
67
        // for the indicator.
68
        $this->info = $info + $this->info;
69
 
70
        // We also need to store the association between the info provided and the sample.
71
        $this->samplesinfo[$sampleid] = array_keys($info);
72
    }
73
 
74
    /**
75
     * Stores in MUC the previously added data and it associates it to the provided $calculable.
76
     *
77
     * @param  \core_analytics\calculable                $calculable
78
     * @param  \core_analytics\local\time_splitting\base $timesplitting
79
     * @param  int                                       $rangeindex
80
     * @return null
81
     */
82
    public function save(\core_analytics\calculable $calculable, \core_analytics\local\time_splitting\base $timesplitting,
83
            int $rangeindex) {
84
 
85
        $calculableclass = get_class($calculable);
86
        $cache = \cache::make('core', 'calculablesinfo');
87
 
88
        foreach ($this->info as $key => $value) {
89
            $datakey = self::get_data_key($calculableclass, $key);
90
 
91
            // We do not overwrite existing data.
92
            if (!$cache->has($datakey)) {
93
                $cache->set($datakey, $value);
94
            }
95
        }
96
 
97
        foreach ($this->samplesinfo as $sampleid => $infokeys) {
98
            $uniquesampleid = $timesplitting->append_rangeindex($sampleid, $rangeindex);
99
            $samplekey = self::get_sample_key($uniquesampleid);
100
 
101
            // Update the cached data adding the new indicator data.
102
            $cacheddata = $cache->get($samplekey) ?: [];
103
            $cacheddata[$calculableclass] = $infokeys;
104
            $cache->set($samplekey, $cacheddata);
105
        }
106
 
107
        // Empty the in-memory arrays now that it is in the cache.
108
        $this->info = [];
109
        $this->samplesinfo = [];
110
    }
111
 
112
    /**
113
     * Pulls the info related to the provided records out from the cache.
114
     *
115
     * Note that this function purges 'calculablesinfo' cache.
116
     *
117
     * @param  \stdClass[] $predictionrecords
118
     * @return array|false
119
     */
120
    public static function pull_info(array $predictionrecords) {
121
 
122
        $cache = \cache::make('core', 'calculablesinfo');
123
 
124
        foreach ($predictionrecords as $uniquesampleid => $predictionrecord) {
125
 
126
            $sampleid = $predictionrecord->sampleid;
127
 
128
            $sampleinfo = $cache->get(self::get_sample_key($uniquesampleid));
129
 
130
            // MUC returns (or should return) copies of the data and we want a single copy of it so
131
            // we store the data here and reference it from each sample. Samples data should not be
132
            // changed afterwards.
133
            $data = [];
134
 
135
            if ($sampleinfo) {
136
                foreach ($sampleinfo as $calculableclass => $infokeys) {
137
 
138
                    foreach ($infokeys as $infokey) {
139
 
140
                        // We don't need to retrieve data back from MUC if we already have it.
141
                        if (!isset($data[$calculableclass][$infokey])) {
142
                            $datakey = self::get_data_key($calculableclass, $infokey);
143
                            $data[$calculableclass][$infokey] = $cache->get($datakey);
144
                        }
145
 
146
                        $samplesdatakey = $calculableclass . ':extradata';
147
                        $samplesdata[$sampleid][$samplesdatakey][$infokey] = & $data[$calculableclass][$infokey];
148
                    }
149
                }
150
            }
151
        }
152
 
153
        // Free memory ASAP. We can replace the purge call by a delete_many if we are interested on allowing
154
        // multiple calls to pull_info passing in different $sampleids.
155
        $cache->purge();
156
 
157
        if (empty($samplesdata)) {
158
            return false;
159
        }
160
 
161
        return $samplesdata;
162
    }
163
 
164
    /**
165
     * Gets the key used to store data.
166
     *
167
     * @param  string       $calculableclass
168
     * @param  string|int   $key
169
     * @return string
170
     */
171
    private static function get_data_key(string $calculableclass, $key): string {
172
        return 'data:' . $calculableclass . ':' . $key;
173
    }
174
 
175
    /**
176
     * Gets the key used to store samples.
177
     *
178
     * @param  string $uniquesampleid
179
     * @return string
180
     */
181
    private static function get_sample_key(string $uniquesampleid): string {
182
        return 'sample:' . $uniquesampleid;
183
    }
184
}