2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Extra information generated during the analysis by calculable elements.
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
25 namespace core_analytics
;
27 defined('MOODLE_INTERNAL') ||
die();
30 * Extra information generated during the analysis by calculable elements.
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.
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
39 class calculation_info
{
49 private $samplesinfo = [];
52 * Adds info related to the current calculation for later use when generating insights.
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.
57 * We store two different arrays so objects that appear multiple times for different samples
58 * appear just once in memory.
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.
64 public function add_shared(int $sampleid, array $info) {
66 // We can safely overwrite the existing keys because the provided info is supposed to be unique
68 $this->info
= $info +
$this->info
;
70 // We also need to store the association between the info provided and the sample.
71 $this->samplesinfo
[$sampleid] = array_keys($info);
75 * Stores in MUC the previously added data and it associates it to the provided $calculable.
77 * @param \core_analytics\calculable $calculable
78 * @param \core_analytics\local\time_splitting\base $timesplitting
79 * @param int $rangeindex
82 public function save(\core_analytics\calculable
$calculable, \core_analytics\local\time_splitting\base
$timesplitting,
85 $calculableclass = get_class($calculable);
86 $cache = \cache
::make('core', 'calculablesinfo');
88 foreach ($this->info
as $key => $value) {
89 $datakey = self
::get_data_key($calculableclass, $key);
91 // We do not overwrite existing data.
92 if (!$cache->has($datakey)) {
93 $cache->set($datakey, $value);
97 foreach ($this->samplesinfo
as $sampleid => $infokeys) {
98 $uniquesampleid = $timesplitting->append_rangeindex($sampleid, $rangeindex);
99 $samplekey = self
::get_sample_key($uniquesampleid);
101 // Update the cached data adding the new indicator data.
102 $cacheddata = $cache->get($samplekey);
103 $cacheddata[$calculableclass] = $infokeys;
104 $cache->set($samplekey, $cacheddata);
107 // Empty the in-memory arrays now that it is in the cache.
109 $this->samplesinfo
= [];
113 * Pulls the info related to the provided records out from the cache.
115 * Note that this function purges 'calculablesinfo' cache.
117 * @param \stdClass[] $predictionrecords
118 * @return array|false
120 public static function pull_info(array $predictionrecords) {
122 $cache = \cache
::make('core', 'calculablesinfo');
124 foreach ($predictionrecords as $uniquesampleid => $predictionrecord) {
126 $sampleid = $predictionrecord->sampleid
;
128 $sampleinfo = $cache->get(self
::get_sample_key($uniquesampleid));
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.
136 foreach ($sampleinfo as $calculableclass => $infokeys) {
138 foreach ($infokeys as $infokey) {
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);
146 $samplesdatakey = $calculableclass . ':extradata';
147 $samplesdata[$sampleid][$samplesdatakey][$infokey] = & $data[$calculableclass][$infokey];
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.
157 if (empty($samplesdata)) {
165 * Gets the key used to store data.
167 * @param string $calculableclass
168 * @param string|int $key
171 private static function get_data_key(string $calculableclass, $key): string {
172 return 'data:' . $calculableclass . ':' . $key;
176 * Gets the key used to store samples.
178 * @param string $uniquesampleid
181 private static function get_sample_key(string $uniquesampleid): string {
182 return 'sample:' . $uniquesampleid;