Added new metric counting man-days effort

The metric counts number of days a unique user made some action.

Implements bp contribution-effort

Change-Id: I00fbb2ab2c0c9617ef2e4d949ac06e7a64facdac
This commit is contained in:
Ilya Shakhat 2014-04-15 15:46:47 +04:00
parent 05c30f0f51
commit 418a4a8140
3 changed files with 71 additions and 15 deletions

View File

@ -13,10 +13,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import collections
import functools import functools
import json import json
import flask import flask
import six
from werkzeug import exceptions from werkzeug import exceptions
from dashboard import helpers from dashboard import helpers
@ -105,6 +107,7 @@ def record_filter(ignore=None, use_default=True):
metrics = parameters.get_parameter(kwargs, 'metric') metrics = parameters.get_parameter(kwargs, 'metric')
if 'all' not in metrics: if 'all' not in metrics:
for metric in metrics: for metric in metrics:
if metric in parameters.METRIC_TO_RECORD_TYPE:
record_ids &= ( record_ids &= (
memory_storage_inst.get_record_ids_by_type( memory_storage_inst.get_record_ids_by_type(
parameters.METRIC_TO_RECORD_TYPE[metric])) parameters.METRIC_TO_RECORD_TYPE[metric]))
@ -194,6 +197,31 @@ def mark_finalize(record):
return new_record return new_record
def man_days_filter(result, record, param_id):
if record['record_type'] == 'commit':
# commit is attributed with the date of the merge which is not an
# effort of the author (author's effort is represented in patches)
return
day = record['date'] // (24 * 3600)
result_by_param = result[record[param_id]]
if 'days' not in result_by_param:
result_by_param['days'] = collections.defaultdict(set)
user = vault.get_user_from_runtime_storage(record['user_id'])
result_by_param['days'][day] |= set([user['seq']])
result_by_param['metric'] = 1
def man_days_finalize(result_item):
metric = 0
for day_set in six.itervalues(result_item['days']):
metric += len(day_set)
del result_item['days']
result_item['metric'] = metric
return result_item
def aggregate_filter(): def aggregate_filter():
def decorator(f): def decorator(f):
@functools.wraps(f) @functools.wraps(f)
@ -212,6 +240,7 @@ def aggregate_filter():
'bpd': (incremental_filter, None), 'bpd': (incremental_filter, None),
'bpc': (incremental_filter, None), 'bpc': (incremental_filter, None),
'members': (incremental_filter, None), 'members': (incremental_filter, None),
'man-days': (man_days_filter, man_days_finalize),
} }
if metric not in metric_to_filters_map: if metric not in metric_to_filters_map:
metric = parameters.get_default('metric') metric = parameters.get_default('metric')

View File

@ -35,6 +35,7 @@ METRIC_LABELS = {
'emails': 'Emails', 'emails': 'Emails',
'bpd': 'Drafted Blueprints', 'bpd': 'Drafted Blueprints',
'bpc': 'Completed Blueprints', 'bpc': 'Completed Blueprints',
'man-days': "Man-days effort"
} }
METRIC_TO_RECORD_TYPE = { METRIC_TO_RECORD_TYPE = {

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import collections
import operator import operator
import os import os
import re import re
@ -88,8 +89,8 @@ def _get_aggregated_stats(records, metric_filter, keys, param_id,
result[record[param_id]]['name'] = record[param_title] result[record[param_id]]['name'] = record[param_title]
response = [r for r in result.values() if r['metric']] response = [r for r in result.values() if r['metric']]
response.sort(key=lambda x: x['metric'], reverse=True)
response = [item for item in map(finalize_handler, response) if item] response = [item for item in map(finalize_handler, response) if item]
response.sort(key=lambda x: x['metric'], reverse=True)
utils.add_index(response, item_filter=lambda x: x['id'] != '*independent') utils.add_index(response, item_filter=lambda x: x['id'] != '*independent')
return response return response
@ -102,7 +103,8 @@ def _get_aggregated_stats(records, metric_filter, keys, param_id,
def get_companies(records, metric_filter, finalize_handler): def get_companies(records, metric_filter, finalize_handler):
return _get_aggregated_stats(records, metric_filter, return _get_aggregated_stats(records, metric_filter,
vault.get_memory_storage().get_companies(), vault.get_memory_storage().get_companies(),
'company_name') 'company_name',
finalize_handler=finalize_handler)
@app.route('/api/1.0/stats/modules') @app.route('/api/1.0/stats/modules')
@ -113,7 +115,7 @@ def get_companies(records, metric_filter, finalize_handler):
def get_modules(records, metric_filter, finalize_handler): def get_modules(records, metric_filter, finalize_handler):
return _get_aggregated_stats(records, metric_filter, return _get_aggregated_stats(records, metric_filter,
vault.get_memory_storage().get_modules(), vault.get_memory_storage().get_modules(),
'module') 'module', finalize_handler=finalize_handler)
def get_core_engineer_branch(user, modules): def get_core_engineer_branch(user, modules):
@ -492,21 +494,45 @@ def timeline(records, **kwargs):
week_stat_commits = dict((c, 0) for c in weeks) week_stat_commits = dict((c, 0) for c in weeks)
week_stat_commits_hl = dict((c, 0) for c in weeks) week_stat_commits_hl = dict((c, 0) for c in weeks)
param = parameters.get_parameter(kwargs, 'metric') metric = parameters.get_parameter(kwargs, 'metric')
if ('commits' in param) or ('loc' in param): if ('commits' in metric) or ('loc' in metric):
handler = lambda record: record['loc'] handler = lambda record: record['loc']
else: else:
handler = lambda record: 0 handler = lambda record: 0
# fill stats with the data # fill stats with the data
if 'man-days' in metric:
# special case for man-day effort metric
release_stat = collections.defaultdict(set)
all_stat = collections.defaultdict(set)
for record in records:
if ((record['record_type'] == 'commit') or
(record['week'] not in weeks)):
continue
day = record['date'] // (24 * 3600)
user = vault.get_user_from_runtime_storage(record['user_id'])
if record['release'] == release_name:
release_stat[day] |= set([user['seq']])
all_stat[day] |= set([user['seq']])
for day, users in six.iteritems(release_stat):
week = utils.timestamp_to_week(day * 24 * 3600)
week_stat_commits_hl[week] += len(users)
for day, users in six.iteritems(all_stat):
week = utils.timestamp_to_week(day * 24 * 3600)
week_stat_commits[week] += len(users)
else:
for record in records: for record in records:
week = record['week'] week = record['week']
if week in weeks: if week in weeks:
week_stat_loc[week] += handler(record) week_stat_loc[week] += handler(record)
week_stat_commits[week] += 1 week_stat_commits[week] += 1
if 'all' == release_name or record['release'] == release_name: if record['release'] == release_name:
week_stat_commits_hl[week] += 1 week_stat_commits_hl[week] += 1
if 'all' == release_name:
week_stat_commits_hl = week_stat_commits
# form arrays in format acceptable to timeline plugin # form arrays in format acceptable to timeline plugin
array_loc = [] array_loc = []
array_commits = [] array_commits = []