Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Extra information generated during the analysis by calculable elements.
 *
 * @package   core_analytics
 * @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace core_analytics;

defined('MOODLE_INTERNAL') || die();

/**
 * Extra information generated during the analysis by calculable elements.
 *
 * The main purpose of this request cache is to allow calculable elements to
 * store data during their calculations for further use at a later stage efficiently.
 *
 * @package   core_analytics
 * @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class calculation_info {

    /**
     * @var array
     */
    private $info = [];

    /**
     * @var mixed[]
     */
    private $samplesinfo = [];

    /**
     * Adds info related to the current calculation for later use when generating insights.
     *
     * Note that the data in $info array is reused across multiple samples, if you want to add data just for this
     * sample you can use the sample id as key.
     *
     * We store two different arrays so objects that appear multiple times for different samples
     * appear just once in memory.
     *
     * @param int    $sampleid  The sample id this data is associated with
     * @param array  $info      The data. Indexed by an id unique across the site. E.g. an activity id.
     * @return null
     */
    public function add_shared(int $sampleid, array $info) {

        // We can safely overwrite the existing keys because the provided info is supposed to be unique
        // for the indicator.
        $this->info = $info + $this->info;

        // We also need to store the association between the info provided and the sample.
        $this->samplesinfo[$sampleid] = array_keys($info);
    }

    /**
     * Stores in MUC the previously added data and it associates it to the provided $calculable.
     *
     * @param  \core_analytics\calculable                $calculable
     * @param  \core_analytics\local\time_splitting\base $timesplitting
     * @param  int                                       $rangeindex
     * @return null
     */
    public function save(\core_analytics\calculable $calculable, \core_analytics\local\time_splitting\base $timesplitting,
            int $rangeindex) {

        $calculableclass = get_class($calculable);
        $cache = \cache::make('core', 'calculablesinfo');

        foreach ($this->info as $key => $value) {
            $datakey = self::get_data_key($calculableclass, $key);

            // We do not overwrite existing data.
            if (!$cache->has($datakey)) {
                $cache->set($datakey, $value);
            }
        }

        foreach ($this->samplesinfo as $sampleid => $infokeys) {
            $uniquesampleid = $timesplitting->append_rangeindex($sampleid, $rangeindex);
            $samplekey = self::get_sample_key($uniquesampleid);

            // Update the cached data adding the new indicator data.
< $cacheddata = $cache->get($samplekey);
> $cacheddata = $cache->get($samplekey) ?: [];
$cacheddata[$calculableclass] = $infokeys; $cache->set($samplekey, $cacheddata); } // Empty the in-memory arrays now that it is in the cache. $this->info = []; $this->samplesinfo = []; } /** * Pulls the info related to the provided records out from the cache. * * Note that this function purges 'calculablesinfo' cache. * * @param \stdClass[] $predictionrecords * @return array|false */ public static function pull_info(array $predictionrecords) { $cache = \cache::make('core', 'calculablesinfo'); foreach ($predictionrecords as $uniquesampleid => $predictionrecord) { $sampleid = $predictionrecord->sampleid; $sampleinfo = $cache->get(self::get_sample_key($uniquesampleid)); // MUC returns (or should return) copies of the data and we want a single copy of it so // we store the data here and reference it from each sample. Samples data should not be // changed afterwards. $data = []; if ($sampleinfo) { foreach ($sampleinfo as $calculableclass => $infokeys) { foreach ($infokeys as $infokey) { // We don't need to retrieve data back from MUC if we already have it. if (!isset($data[$calculableclass][$infokey])) { $datakey = self::get_data_key($calculableclass, $infokey); $data[$calculableclass][$infokey] = $cache->get($datakey); } $samplesdatakey = $calculableclass . ':extradata'; $samplesdata[$sampleid][$samplesdatakey][$infokey] = & $data[$calculableclass][$infokey]; } } } } // Free memory ASAP. We can replace the purge call by a delete_many if we are interested on allowing // multiple calls to pull_info passing in different $sampleids. $cache->purge(); if (empty($samplesdata)) { return false; } return $samplesdata; } /** * Gets the key used to store data. * * @param string $calculableclass * @param string|int $key * @return string */ private static function get_data_key(string $calculableclass, $key): string { return 'data:' . $calculableclass . ':' . $key; } /** * Gets the key used to store samples. * * @param string $uniquesampleid * @return string */ private static function get_sample_key(string $uniquesampleid): string { return 'sample:' . $uniquesampleid; } }