* Finish measurement collection design.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/meas.class.php';
|
||||
|
||||
/**
|
||||
* 按产品统计的每月完成研发需求总数。
|
||||
* The count of monthly done stories in product.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @author qixinzhi <qixinzhi@easycorp.ltd>
|
||||
* @package
|
||||
* @license LGPL
|
||||
* @Link https://www.zentao.net
|
||||
*/
|
||||
class counts_of_product extends measBase
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->collection = 'realTime';
|
||||
$this->range = 'glob';
|
||||
$this->purpose = 'scale';
|
||||
|
||||
$this->table = TABLE_STORY;
|
||||
|
||||
$operator = 'and';
|
||||
$conditions = array();
|
||||
$conditions[] = array('table' => TABLE_STORY, 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
$conditions[] = array('table' => TABLE_STORY, 'field' => 'closedReason', 'operator' => '=', 'value' => 'done');
|
||||
|
||||
$this->filters = array('operator' => $operator, 'conditions' => $conditions);
|
||||
|
||||
|
||||
$this->indicators = array();
|
||||
$this->indicators[] = array('table' => TABLE_STORY, 'field' => 'product', 'aggregate' => 'group');
|
||||
$this->indicators[] = array('table' => TABLE_STORY, 'field' => 'closedDate', 'aggregate' => 'group');
|
||||
$this->indicators[] = array('table' => TABLE_STORY, 'field' => 'id', 'aggregate' => 'count');
|
||||
|
||||
$this->result = array();
|
||||
}
|
||||
|
||||
public function calculate($data, $multiple = false)
|
||||
{
|
||||
if($multiple) foreach($data as $item) $this->calculate($item);
|
||||
|
||||
if(!$this->parseCondition($data, $this->filters)) return;
|
||||
|
||||
foreach($this->indicators as $index => $indicator)
|
||||
{
|
||||
$field = $indicator['field'];
|
||||
$agg = $indicator['aggregate'];
|
||||
|
||||
if($agg == 'group') continue;
|
||||
|
||||
$productID = $data['product'];
|
||||
$month = date('Y-m', strtotime($data['closedDate']));
|
||||
|
||||
if(!isset($this->result[$productID])) $this->result[$productID] = array();
|
||||
if(!isset($this->result[$productID][$month])) $this->result[$productID][$month] = 0;
|
||||
|
||||
$this->result[$productID][$month] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/meas.class.php';
|
||||
|
||||
/**
|
||||
* 所有产品总数。
|
||||
* The count of all product.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @author qixinzhi <qixinzhi@easycorp.ltd>
|
||||
* @package
|
||||
* @license LGPL
|
||||
* @Link https://www.zentao.net
|
||||
*/
|
||||
class counts_of_product extends measBase
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->collection = 'realTime';
|
||||
$this->range = 'glob';
|
||||
$this->purpose = 'scale';
|
||||
|
||||
$this->table = TABLE_PRODUCCT;
|
||||
|
||||
$operator = 'and';
|
||||
$conditions = array();
|
||||
$conditions[] = array('table' => TABLE_PRODUCT, 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
$conditions[] = array('table' => TABLE_PRODUCT, 'field' => 'shadow', 'operator' => '=', 'value' => '0');
|
||||
|
||||
$this->filters = array('operator' => $operator, 'conditions' => $conditions);
|
||||
|
||||
$this->indicators = array();
|
||||
$this->indicators[] = array('table' => TABLE_PRODUCT, 'field' => 'id', 'aggregate' => 'count');
|
||||
|
||||
$this->result = 0;
|
||||
}
|
||||
|
||||
public function calculate($data, $multiple = false)
|
||||
{
|
||||
if($multiple) foreach($data as $item) $this->calculate($item);
|
||||
|
||||
if(!$this->parseCondition($data)) return;
|
||||
|
||||
$this->result += 1;
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/meas.class.php';
|
||||
|
||||
/**
|
||||
* 年发布的总数。
|
||||
* The count of product release per year.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @author qixinzhi <qixinzhi@easycorp.ltd>
|
||||
* @package
|
||||
* @license LGPL
|
||||
* @Link https://www.zentao.net
|
||||
*/
|
||||
class counts_of_release_per_year extends measBase
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->collection = 'realTime';
|
||||
$this->range = 'glob';
|
||||
$this->purpose = 'scale';
|
||||
|
||||
$this->table = TABLE_RELEASE;
|
||||
$this->leftJoins = array();
|
||||
$this->leftJoins = array('table' => TABLE_PRODUCT, 'alias' => 't2', 'on' => 't2.id=t1.product');
|
||||
$this->leftJoins = array('table' => TABLE_PROJECT, 'alias' => 't3', 'on' => 't3.id=t1.project');
|
||||
|
||||
// $this->leftJoins = array('table' => TABLE_PROJECT, 'mainField' => 'project', 'subField' => 'id', 'operator' => '=');
|
||||
// $this->leftJoins = array('table' => TABLE_PROJECT, 'mainField' => 'project', 'subField' => 'id', 'operator' => 'IN');
|
||||
// $this->leftJoins = array('table' => TABLE_PROJECT, 'mainField' => 'project', 'subField' => 'id', 'operator' => 'NOT IN');
|
||||
// $this->leftJoins = array('table' => TABLE_PROJECT, 'mainField' => 'project', 'subField' => 'id', 'operator' => 'FIND_IN_SET');
|
||||
|
||||
$operator = 'and';
|
||||
$conditions = array();
|
||||
$conditions[] = array('table' => 't1', 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
$conditions[] = array('table' => 't2', 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
$conditions[] = array('table' => 't3', 'field' => 'hasProduct', 'operator' => '=', 'value' => '1');
|
||||
|
||||
$this->filters = array('operator' => $operator, 'conditions' => $conditions);
|
||||
|
||||
|
||||
$this->indicators = array();
|
||||
$this->indicators[] = array('table' => TABLE_RELEASE, 'field' => 'id', 'aggregate' => 'count', 'value' => 0);
|
||||
|
||||
$this->result = 0;
|
||||
}
|
||||
|
||||
public function calculate($data, $multiple = false)
|
||||
{
|
||||
if($multiple) foreach($data as $item) $this->calculate($item);
|
||||
|
||||
if(!$this->parseCondition($data, $this->filters)) return;
|
||||
|
||||
foreach($this->indicators as $index => $indicator)
|
||||
{
|
||||
$this->result += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 度量项定义基类。
|
||||
* Base class of measurement defination.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @author qixinzhi <qixinzhi@easycorp.ltd>
|
||||
* @package
|
||||
* @license LGPL
|
||||
* @Link https://www.zentao.net
|
||||
*/
|
||||
class measBase
|
||||
{
|
||||
/**
|
||||
* 收集方式。
|
||||
* 可选项:实时更新 | 定时任务 | 动作触发
|
||||
* Collection methods of measurement.
|
||||
* Options: realTime | timedTask | actionTrigger
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $collection;
|
||||
|
||||
/**
|
||||
* 范围。
|
||||
* 可选项:全局 | 项目集 | 项目 | 执行 | 产品 | 个人 | 团队
|
||||
* Range of measurement.
|
||||
* Options: glob | prog | proj | exec | prod | user | dept
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $range;
|
||||
|
||||
/**
|
||||
* 目的。
|
||||
* 可选项:规模估算 | 质量控制 | 工时统计 | 成本计算 | 效率提升 | 工期控制
|
||||
* Purpose of measurement.
|
||||
* Options: scale | qc | hour | cost | rate | time
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $purpose;
|
||||
|
||||
/**
|
||||
* 收集频率。
|
||||
* Frequence of collection.
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $collectConf;
|
||||
|
||||
/**
|
||||
* 数据主表。
|
||||
* Main table.
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* 连接表。
|
||||
* Left join tables.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $leftJoins;
|
||||
|
||||
/**
|
||||
* 过滤条件。
|
||||
* Filters of data.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $filters;
|
||||
|
||||
/**
|
||||
* 分组方式。
|
||||
* Groups of data.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $groups;
|
||||
|
||||
/**
|
||||
* 指标。
|
||||
* Indicators of measurement.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $indicators;
|
||||
|
||||
/**
|
||||
* 指标结果。
|
||||
* Result of indicators.
|
||||
*
|
||||
* @var array|float
|
||||
* @access public
|
||||
*/
|
||||
public $result;
|
||||
|
||||
/**
|
||||
* 根据主表和连接表,抽取数据库表和字段。
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getTables()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前主表和连接表以及过滤条件涉及到的字段
|
||||
* 格式为: array('t2.field1')
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getFields()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主表和连接表构建的sql字符串,生成MD5值(16位),用作当前度量项的特征值,目的是为了归类度量项
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getHash()
|
||||
{
|
||||
/*
|
||||
* select * from mainTable t1
|
||||
* left join subTable1 t2 on t2.id = t1.field1
|
||||
* left join subTable2 t3 on t3.id = t1.field2
|
||||
* left join subTable3 t4 on t4.id = t1.field3
|
||||
* ····
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主表和连接表,生成sql。
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getSql($selectFields)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算度量项。
|
||||
*
|
||||
* @param array $data
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function calculate($data)
|
||||
{
|
||||
/* 1. 判断过滤条件是否满足,不满足则不计算。*/
|
||||
|
||||
/* 2. 遍历指标,进行计算。*/
|
||||
|
||||
/* 2.1 如果指标中包含了分组类型,则在result中维护分组。*/
|
||||
/* 2.2 指标包含了筛选条件,满足筛选条件时记录数据, 否则直接记录数据。*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取度量项指标结果。
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析过滤条件,返回当前数据满足过滤条件的bool值。
|
||||
*
|
||||
* @param array $data
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function parseCondition($data)
|
||||
{
|
||||
$operator = $this->filter['operator'];
|
||||
$conditions = $this->filter['conditions'];
|
||||
|
||||
return $this->traverseCondition($data, $operator, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指标属性获取对应指标的数据值。
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $indicator
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getIndicatorValue($data, $indicator)
|
||||
{
|
||||
$table = $indicator['table'];
|
||||
$field = $indicator['field'];
|
||||
|
||||
return $this->getValue($data, $table, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归遍历过滤条件,返回过滤条件的bool值。
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $operator
|
||||
* @param int $conditions
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function traverseCondition($data, $operator, $conditions)
|
||||
{
|
||||
$judgeBool = array();
|
||||
foreach($conditions as $condition)
|
||||
{
|
||||
if(isset($condition['conditions']))
|
||||
{
|
||||
$subOperator = $condition['operator'];
|
||||
$subConditions = $condition['conditions'];
|
||||
$judgeBool[] = $this->traverseCondition($data, $subOperator, $subConditions);
|
||||
continue;
|
||||
}
|
||||
|
||||
$table = $condition['table'];
|
||||
$field = $condition['field'];
|
||||
$value = $condition['value'];
|
||||
|
||||
$dataValue = $this->getValue($data, $table, $field);
|
||||
$judgeBool[] = self::getBoolByCondition($condition['operator'], $dataValue, $value);
|
||||
|
||||
// $judgeBool[] = eval("return {$dataValue} $condition['operator'] $value;");
|
||||
}
|
||||
|
||||
$result = $operator == 'and' ? true : false;
|
||||
foreach($judgeBool as $flag)
|
||||
{
|
||||
$result = $operator == 'and' ? ($result && $flag) : ($result || $flag);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段和表,获取字段的数据值。
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function getValue($data, $table, $field)
|
||||
{
|
||||
return is_array($data) ? $data[$field] : ((array)$data)[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数计算bool值。
|
||||
*
|
||||
* @param string $operator
|
||||
* @param string|number $left
|
||||
* @param string|number $right
|
||||
* @static
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function getBoolByCondition($operator, $left, $right)
|
||||
{
|
||||
$flag = false;
|
||||
switch($operator)
|
||||
{
|
||||
case '=':
|
||||
$flag = $left == $right;
|
||||
break;
|
||||
case '>':
|
||||
$flag = $left > $right;
|
||||
break;
|
||||
case '>=':
|
||||
$flag = $left >= $right;
|
||||
break;
|
||||
case '<':
|
||||
$flag = $left < $right;
|
||||
break;
|
||||
case '<=':
|
||||
$flag = $left <= $right;
|
||||
break;
|
||||
case '!=':
|
||||
$flag = $left != $right;
|
||||
break;
|
||||
}
|
||||
return $flag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
$sqlite = new Sqlite(); // 连接数据库
|
||||
|
||||
golbal $measPaths;
|
||||
|
||||
$measGroup = array();
|
||||
foreach($measPaths as $className => $path)
|
||||
{
|
||||
include_once $path;
|
||||
$measObj = new $className();
|
||||
|
||||
/* 获取当前度量项的特征值,本质上是一个md5字符串,是对主表和连接表的字符串进行md5计算。*/
|
||||
$hash = $measObj->getHash();
|
||||
|
||||
if(!isset($measGroup[$hash])) $measGroup[$hash] = array('sql' => '', 'measList' => array(), 'fields' => array());
|
||||
$measGroup[$hash]['measList'][] = $measObj;
|
||||
$measGroup[$hash]['fields'][] = $measObj->getFields();
|
||||
}
|
||||
|
||||
/* 解析生成数据查询sql。*/
|
||||
foreach($measGroup as $hash => $measInfo)
|
||||
{
|
||||
$uniqueFields = array_unique($measInfo['fields']);
|
||||
$firstMeasObj = current($measInfo['measList']);
|
||||
$measInfo['sql'] = $firstMeasObj->getSql($uniqueFields);
|
||||
}
|
||||
|
||||
/* 筛选出当前场景下需要收集的度量项。*/
|
||||
$readyMeasGroup = array();
|
||||
foreach($measGroup as $measInfo)
|
||||
{
|
||||
$measList = array();
|
||||
foreach($measInfo['measList'] => $measObj)
|
||||
{
|
||||
if(!isReady($measObj->collectConf)) continue;
|
||||
$measList[] = $measObj;
|
||||
}
|
||||
if(!empty($measList)) $readyMeasGroup[] = array('sql' => $measInfo['sql'], 'measList' => $measList, 'fields' => $measInfo['fields']);
|
||||
}
|
||||
|
||||
$measRecord = array();
|
||||
|
||||
foreach($readyMeasGroup as $hash => $measInfo)
|
||||
{
|
||||
$sql = $measInfo['sql'];
|
||||
|
||||
/* 读数据库获取数据。*/
|
||||
$dataList = $sqlite->query($sql);
|
||||
|
||||
foreach($dataList => $item)
|
||||
{
|
||||
foreach($measInfo['measList'] as $measObj)
|
||||
{
|
||||
$measObj->calculate($item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($measInfo['measList'] as $measObj)
|
||||
{
|
||||
$result = $measObj->getResult();
|
||||
$measRecord = array_merge($measRecord, formatMeasResult($result));
|
||||
}
|
||||
|
||||
// foreach($measInfo['measList'] as $measObj)
|
||||
// {
|
||||
// $measObj->calculate(&$dataList);
|
||||
// $result = $measObj->getResult();
|
||||
// $measRecord = array_merge($measRecord, formatMeasResult($result));
|
||||
// }
|
||||
}
|
||||
|
||||
foreach($measRecord as $record) $this->dao->insert(TABLE_MEASRECORD)->data($record)->exec();
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/meas.class.php';
|
||||
|
||||
/**
|
||||
* 项目消耗的工时相对于每人每日可用工时的比例。
|
||||
* The radio of consumed hours compare daily person hours in project.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @author qixinzhi <qixinzhi@easycorp.ltd>
|
||||
* @package
|
||||
* @license LGPL
|
||||
* @Link https://www.zentao.net
|
||||
*/
|
||||
class ratio_of_consumed_hours_in_project extends measBase
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->collection = 'realTime';
|
||||
$this->range = 'glob';
|
||||
$this->purpose = 'scale';
|
||||
|
||||
$this->table = TABLE_TASK;
|
||||
|
||||
$t4 = "(select t1.root, sum(t1.hours) / count(t1.id) as perHours from zt_team t1 left join zt_user t2 on t2.account = t1.account where t2.deleted='0' group by t1.root)";
|
||||
|
||||
$leftJoins = array();
|
||||
$leftJoins = array('table' => TABLE_PROJECT, 'alias' => 't2', 'on' => "t2.id=t1.project and t2.type='sprint'");
|
||||
$leftJoins = array('table' => TABLE_PROJECT, 'alias' => 't3', 'on' => "t3.id=t2.parent");
|
||||
$leftJoins = array('table' => $t4, 'alias' => 't4', 'on' => "t2.id=t4.root");
|
||||
|
||||
$this->leftJoins = $leftJoins;
|
||||
|
||||
$operator = 'and';
|
||||
$conditions = array();
|
||||
$conditions[] = array('table' => 't1', 'field' => 'parent', 'operator' => '!=', 'value' => '-1');
|
||||
$conditions[] = array('table' => 't1', 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
$conditions[] = array('table' => 't2', 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
$conditions[] = array('table' => 't3', 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
|
||||
$this->filters = array('operator' => $operator, 'conditions' => $conditions);
|
||||
|
||||
$this->indicators = array();
|
||||
$this->indicators[] = array('table' => 't3', 'field' => 'id', 'aggregate' => 'group');
|
||||
$this->indicators[] = array('table' => 't1', 'field' => 'consumed', 'aggregate' => 'sum');
|
||||
$this->indicators[] = array('table' => 't4', 'field' => 'perHours', 'aggregate' => 'sum');
|
||||
|
||||
$this->result = array();
|
||||
}
|
||||
|
||||
public function calculate($data, $multiple = false)
|
||||
{
|
||||
if($multiple) foreach($data as $item) $this->calculate($item);
|
||||
|
||||
if(!$this->parseCondition($data)) return;
|
||||
|
||||
foreach($this->indicators as $index => $indicator)
|
||||
{
|
||||
$field = $indicator['field'];
|
||||
$agg = $indicator['aggregate'];
|
||||
|
||||
if($agg == 'group') continue;
|
||||
|
||||
$value = $this->getIndicatorValue($data, $indicator);
|
||||
$projectID = $data['t3_id'];
|
||||
|
||||
if(!isset($this->result[$projectID])) $this->result[$projectID] = array('consumed' => 0, 'perHours' => 0);
|
||||
$this->result[$projectID][$field] += (float)$value;
|
||||
}
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach($this->result as $projectID => $item)
|
||||
{
|
||||
$result[$projectID] = $item['perHours'] == 0 ? 0 : $item['consumed'] / $item['perHours'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/meas.class.php';
|
||||
|
||||
/**
|
||||
* 按产品统计的已发布需求数相对于研发需求总数的比例。
|
||||
* The number of published requirements by product relative to the total number of R&D requirements.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @author qixinzhi <qixinzhi@easycorp.ltd>
|
||||
* @package
|
||||
* @license LGPL
|
||||
* @Link https://www.zentao.net
|
||||
*/
|
||||
class ratio_of_released_story_in_product extends measBase
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->collection = 'realTime';
|
||||
$this->range = 'glob';
|
||||
$this->purpose = 'scale';
|
||||
|
||||
$this->table = TABLE_STORY;
|
||||
|
||||
$operator = 'and';
|
||||
$conditions = array();
|
||||
$conditions[] = array('table' => TABLE_STORY, 'field' => 'deleted', 'operator' => '=', 'value' => '0');
|
||||
|
||||
|
||||
$this->filters = array('operator' => $operator, 'conditions' => $conditions);
|
||||
|
||||
$this->indicators = array();
|
||||
$this->indicators[] = array('table' => TABLE_STORY, 'field' => 'product', 'aggregate' => 'group');
|
||||
$this->indicators[] = array('table' => TABLE_STORY, 'field' => 'stage', 'aggregate' => 'count', 'condition' => array('operator' => '=', 'value' => 'released'));
|
||||
$this->indicators[] = array('table' => TABLE_STORY, 'field' => 'closedReason', 'aggregate' => 'count', 'condition' => array('operator' => '=', 'value' => 'done'));
|
||||
$this->indicators[] = array('table' => TABLE_STORY, 'field' => 'status', 'aggregate' => 'count', 'condition' => array('operator' => '!=', 'value' => 'closed'));
|
||||
|
||||
$this->result = array();
|
||||
}
|
||||
|
||||
public function calculate($data, $multiple = false)
|
||||
{
|
||||
if($multiple) foreach($data as $item) $this->calculate($item);
|
||||
|
||||
if(!$this->parseCondition($data)) return;
|
||||
|
||||
foreach($this->indicators as $index => $indicator)
|
||||
{
|
||||
$field = $indicator['field'];
|
||||
$agg = $indicator['aggregate'];
|
||||
|
||||
if($agg == 'group') continue;
|
||||
|
||||
$value = $this->getIndicatorValue($data, $indicator);
|
||||
$productID = $data['product'];
|
||||
|
||||
if(!isset($this->result[$productID])) $this->result[$productID] = array('stage' => 0, 'closedReason' => 0, 'status' => 0);
|
||||
|
||||
$operator = $indicator['condition']['operator'];
|
||||
$rightValue = $indicator['condition']['value'];
|
||||
|
||||
if(self::getBoolByCondition($operator, $value, $rightValue)) $this->result[$productID][$field] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach($this->result as $productID => $item)
|
||||
{
|
||||
$result[$productID] = ($item['closedReason'] + $item['status']) == 0 ? 0 : $item['stage'] / ($item['closedReason'] + $item['status']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user