indefero/src/IDF/ActivityTaxonomy.php
Thomas Keller 65aa830c87 If a project never saved specific tab access rights so far, the specific
access rights setting is not existant and therefor the projects' activity
calculation is always skipped. This should actually not happen.
2011-12-24 03:03:07 +01:00

156 lines
5.9 KiB
PHP

<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero 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 2 of the License, or
# (at your option) any later version.
#
# InDefero 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Class that calculates the activity value for all projects on a
* specific date and time.
*
* We do this by counting adds or updates of database objects in
* the particular section (according to the timeline) and relate this
* value to the overall activity of a section in the forge.
*
* To illustrate the behaviour, a simple example could be a forge with
* only two projects that both have only issue tracking enabled.
* The first project created or updated 10 tickets during the past period,
* the other 20. The activity index for the first should therefor be
* calculated as 0.33 and the second as 0.66.
* Note that this simple example doesn't take activity in other
* sections into account, so the the total activity of all projects
* for a certain time period might add up to more than 1.0.
*
* @author tommyd
*/
class IDF_ActivityTaxonomy
{
public static function recalculateTaxnomies(DateTime $date)
{
//
// query and normalize the section weights
//
$sectionWeights = Pluf::f('activity_section_weights', array());
$allWeights = array_sum($sectionWeights);
if ($allWeights == 0) {
throw new LogicException('the sum of all "activity_section_weights" must not be 0');
}
foreach ($sectionWeights as $section => $weight) {
$sectionWeights[$section] = $weight / (float) $allWeights;
}
//
// determine the date boundaries
//
$lookback = Pluf::f('activity_lookback', 0);
if ($lookback < 1) {
throw new LogicException('lookback must be greater or equal to 1');
}
$dateCopy = new DateTime();
$dateCopy->setTimestamp($date->getTimestamp());
$dateBoundaries = array(
$dateCopy->format('Y-m-d 23:59:59'),
$dateCopy->sub(new DateInterval('P'.$lookback.'D'))->format('Y-m-d 00:00:00')
);
//
// now recalculate the values for all projects
//
$projects = Pluf::factory('IDF_Project')->getList();
foreach ($projects as $project) {
self::recalculateTaxonomy($date, $project, $dateBoundaries, $sectionWeights);
}
}
private static function recalculateTaxonomy(DateTime $date, IDF_Project $project, array $dateBoundaries, array $sectionWeights)
{
$conf = new IDF_Conf();
$conf->setProject($project);
$sectionClasses = array(
'source' => array('IDF_Commit'),
'issues' => array('IDF_Issue'),
'wiki' => array('IDF_Wiki_Page', 'IDF_Wiki_Resource'),
'review' => array('IDF_Review'),
'downloads' => array('IDF_Upload')
);
$value = 0;
foreach ($sectionWeights as $section => $weight) {
// skip closed / non-existant sections
if ($conf->getVal($section.'_access_rights') === 'none')
continue;
if (!array_key_exists($section, $sectionClasses))
continue;
$sectionValue = self::calculateActivityValue(
$dateBoundaries, $sectionClasses[$section], $project->id);
$value = ((1 - $weight) * $value) + ($weight * $sectionValue);
}
echo "project {$project->name} has an activity value of $value\n";
$sql = new Pluf_SQL('project=%s AND date=%s', array($project->id, $date->format('Y-m-d')));
$activity = Pluf::factory('IDF_ProjectActivity')->getOne(array('filter' => $sql->gen()));
if ($activity == null) {
$activity = new IDF_ProjectActivity();
$activity->project = $project;
$activity->date = $date->format('Y-m-d');
$activity->value = $value;
$activity->create();
} else {
$activity->value = $value;
$activity->update();
}
}
private static function calculateActivityValue(array $dateBoundaries, array $classes, $projectId)
{
$allCount = self::countActivityFor($dateBoundaries, $classes);
if ($allCount == 0) return 0;
$prjCount = self::countActivityFor($dateBoundaries, $classes, $projectId);
return $prjCount / (float) $allCount;
}
private static function countActivityFor(array $dateBoundaries, array $classes, $projectId = null)
{
static $cache = array();
$argIdent = md5(serialize(func_get_args()));
if (array_key_exists($argIdent, $cache)) {
return $cache[$argIdent];
}
$cache[$argIdent] = 0;
list($higher, $lower) = $dateBoundaries;
$sql = new Pluf_SQL('model_class IN ("'.implode('","', $classes).'") '.
'AND creation_dtime >= %s AND creation_dtime <= %s',
array($lower, $higher));
if ($projectId !== null) {
$sql->SAnd(new Pluf_SQL('project=%s', array($projectId)));
}
$cache[$argIdent] = Pluf::factory('IDF_Timeline')->getCount(array('filter' => $sql->gen()));
return $cache[$argIdent];
}
}