drupalorg-1.0.x-dev/src/Utilities/ActiveInstalls.php
src/Utilities/ActiveInstalls.php
<?php
namespace Drupal\drupalorg\Utilities;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\node\Entity\Node;
/**
* Utility functions to calculate and populate the active installs per branch.
*/
class ActiveInstalls {
/**
* Calculate and save new usage data for projects in a plain-text field.
*
* Temporary function as the modules managing this are not ported to D9. We
* just migrate the data and then use it raw.
*
* Recommended to be used in cron as it could timeout due to the number of
* projects (~15k) and their releases.
*
* @param int $timestamp
* Timestamp to check from, otherwise we'll take the most recent one.
* @param bool $verbose
* Add verbosity to the logger.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public static function calculateNewActiveInstalls($timestamp = NULL, $verbose = FALSE) {
$memory_cache = \Drupal::service('entity.memory_cache');
$memory_cache_clear_counter = 0;
$last_timestamp = $timestamp ?? self::weeklyUsageWeekTimestamps()[0] ?? FALSE;
$verbose && \Drupal::logger('drupalorg')->info('(Active Installs) Last timestamp: @last_timestamp', [
'@last_timestamp' => date('c', $last_timestamp),
]);
if ($last_timestamp) {
$last_timestamp_date = (new \DateTime())->setTimestamp($last_timestamp);
$date_time_diff = (new DrupalDateTime())->diff($last_timestamp_date);
// Difference older than a week.
if ($date_time_diff->days > 7) {
$results = \Drupal::database()
->select('project_usage_week_release', 'puwr')
->fields('puwr', ['project_id'])
->distinct()
->condition('timestamp', $last_timestamp, '>=')
->execute()
->fetchCol();
$verbose && \Drupal::logger('drupalorg')->info('(Active Installs) Results: @results', [
'@results' => count($results),
]);
if (!empty($results)) {
foreach ($results as $project_id) {
$project = Node::load($project_id);
$weekly_usage = self::weeklyUsagePerProject($project_id);
$project_usage = array_shift($weekly_usage);
$total = 0;
foreach ($project_usage as $branch_usage) {
$total += $branch_usage;
}
$verbose && \Drupal::logger('drupalorg')->info('(Active Installs) Project: @project // Total: @total // Usage: @usage', [
'@project' => $project ? $project->label() : $project_id,
'@total' => $total,
'@usage' => Json::encode($project_usage),
]);
if ($project) {
$changed = FALSE;
if ($project->hasField('field_active_installs')) {
$changed = TRUE;
$project->set('field_active_installs', Json::encode($project_usage));
}
if ($project->hasField('field_active_installs_total')) {
$changed = TRUE;
$project->set('field_active_installs_total', $total);
}
if ($changed) {
$project->save();
}
}
// Reset the cache in order to free memory as we progress.
$memory_cache_clear_counter++;
if ($memory_cache_clear_counter > 100) {
$memory_cache->deleteAll();
$memory_cache_clear_counter = 0;
}
}
}
}
}
}
/**
* Retrieves project usage information by project.
*
* Temporary function as the modules managing this are not ported to D9. We
* just migrate the data and then use it raw.
*
* @param int $id
* Id of the project to check.
* @param int $weeks_to_show
* Number of weeks to get stats from.
*
* @return array
* Usage information for the project organised by releases.
*/
protected static function weeklyUsagePerProject($id, $weeks_to_show = 1) {
// Limit weeks that can be retrieved.
$weeks_to_show = ($weeks_to_show > 4) ? 4 : $weeks_to_show;
$cache = \Drupal::cache();
$cache_key = 'drupalorg:usage_per_project:' . $id . ':' . $weeks_to_show;
if ($result = $cache->get($cache_key)) {
return $result->data;
}
$weeks = self::weeklyUsageWeekTimestamps($weeks_to_show);
$result = [];
foreach ($weeks as $week) {
$result[$week] = self::getUsageByReleasesPerProjectPerWeek($id, $week);
}
$cache->set($cache_key, $result, strtotime('+1 week'));
return $result;
}
/**
* Get array of timestamps used in the weekly tracking.
*
* Temporary function as the modules managing this are not ported to D9. We
* just migrate the data and then use it raw.
*
* @param int $number_of_weekly_timestamps
* Number of weeks to get.
*
* @return array
* List of timestamps sorted by most recent.
*/
protected static function weeklyUsageWeekTimestamps($number_of_weekly_timestamps = 1) {
$number_of_weekly_timestamps = abs((int) $number_of_weekly_timestamps);
$cache = \Drupal::cache();
$cache_key = 'drupalorg:weekly_timestamps:' . $number_of_weekly_timestamps;
$weeks = $cache->get($cache_key);
if ($weeks && !empty($weeks->data)) {
return $weeks->data;
}
$results = \Drupal::database()
->select('project_usage_week_release', 'puwr')
->fields('puwr', ['timestamp'])
->distinct()
->orderBy('timestamp', 'DESC')
->range(0, $number_of_weekly_timestamps)
->execute()
->fetchCol();
$weeks = [];
foreach ($results as $result) {
$weeks[] = $result;
}
// Cache it but make it invalid in one week.
$cache->set($cache_key, $weeks, strtotime('+1 week'));
return $weeks;
}
/**
* Retrieves usage data for a project and a week.
*
* @param int $id
* Id of the project.
* @param int $week
* Week to fetch data from.
*
* @return array
* Releases active installs information for the given week and project.
*/
protected static function getUsageByReleasesPerProjectPerWeek($id, $week) {
/** @var \Drupal\drupalorg\ProjectService $project_service */
$project_service = \Drupal::service('drupalorg.project_service');
$project = Node::load($id);
return ($project && $project_service->isProject($project)) ?
$project_service->getWeeklyUsage($project, $week, TRUE) :
[];
}
}
