Extend review report with more contribution data
Added columns on number of posted patches, number of commits and sent emails. Added summary for contribution statistics Part of blueprint extend-russell-report Change-Id: Ie496ee51ab3bfebb5df9b9980c5f9fd5e3a93068
This commit is contained in:
parent
e6eb64af82
commit
ac05b179ca
@ -183,15 +183,20 @@ def aggregate_filter():
|
|||||||
mark_distribution.append('0')
|
mark_distribution.append('0')
|
||||||
new_record[key] = 0
|
new_record[key] = 0
|
||||||
|
|
||||||
positive_ratio = ' (%.1f%%)' % (
|
|
||||||
(positive * 100.0) / record['metric'])
|
|
||||||
new_record['mark_ratio'] = (
|
|
||||||
'|'.join(mark_distribution) + positive_ratio)
|
|
||||||
new_record['positive_ratio'] = positive_ratio
|
|
||||||
new_record['disagreements'] = record.get('disagreements', 0)
|
new_record['disagreements'] = record.get('disagreements', 0)
|
||||||
new_record['disagreement_ratio'] = '%.1f%%' % (
|
if record['metric']:
|
||||||
(record.get('disagreements', 0) * 100.0) / record['metric']
|
positive_ratio = '%.1f%%' % (
|
||||||
)
|
(positive * 100.0) / record['metric'])
|
||||||
|
new_record['disagreement_ratio'] = '%.1f%%' % (
|
||||||
|
(record.get('disagreements', 0) * 100.0) /
|
||||||
|
record['metric'])
|
||||||
|
else:
|
||||||
|
positive_ratio = helpers.INFINITY_HTML
|
||||||
|
new_record['disagreement_ratio'] = helpers.INFINITY_HTML
|
||||||
|
new_record['mark_ratio'] = ('|'.join(mark_distribution) +
|
||||||
|
' (' + positive_ratio + ')')
|
||||||
|
new_record['positive_ratio'] = positive_ratio
|
||||||
|
|
||||||
return new_record
|
return new_record
|
||||||
|
|
||||||
metric_param = (flask.request.args.get('metric') or
|
metric_param = (flask.request.args.get('metric') or
|
||||||
|
@ -24,6 +24,8 @@ from dashboard import vault
|
|||||||
from stackalytics.processor import utils
|
from stackalytics.processor import utils
|
||||||
|
|
||||||
|
|
||||||
|
INFINITY_HTML = '∞'
|
||||||
|
|
||||||
gravatar = gravatar_ext.Gravatar(None, size=64, rating='g', default='wavatar')
|
gravatar = gravatar_ext.Gravatar(None, size=64, rating='g', default='wavatar')
|
||||||
|
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ function make_uri(uri, options) {
|
|||||||
$.extend(ops, options);
|
$.extend(ops, options);
|
||||||
}
|
}
|
||||||
var str = $.map(ops,function (val, index) {
|
var str = $.map(ops,function (val, index) {
|
||||||
return index + "=" + val;
|
return index + "=" + encodeURIComponent(val).toLowerCase();
|
||||||
}).join("&");
|
}).join("&");
|
||||||
|
|
||||||
return (str == "") ? uri : uri + "?" + str;
|
return (str == "") ? uri : uri + "?" + str;
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
{% extends "reports/base_report.html" %}
|
{% extends "reports/base_report.html" %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
Reviews for the last {{ days }} days in {{ module }}
|
Contribution into {{ module }} for the last {{ days }} days
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var table_column_names = ["index", "link", "metric", "-2", "-1", "1", "2", "A", "positive_ratio", "disagreements", "disagreement_ratio"];
|
var table_column_names = ["index", "link", "metric", "-2", "-1", "1", "2", "A", "positive_ratio", "disagreements", "disagreement_ratio",
|
||||||
|
"review_ratio", "commit", "email"];
|
||||||
var table_id = "review_stats_table";
|
var table_id = "review_stats_table";
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -28,15 +29,20 @@ Reviews for the last {{ days }} days in {{ module }}
|
|||||||
var summary = {
|
var summary = {
|
||||||
'marks': 0,
|
'marks': 0,
|
||||||
'core_marks': 0,
|
'core_marks': 0,
|
||||||
'reviewers': tableData.length,
|
'reviewers': 0,
|
||||||
'core_reviewers': 0
|
'core_reviewers': 0,
|
||||||
|
'commits': 0,
|
||||||
|
'reviews': 0,
|
||||||
|
'patch_count': 0,
|
||||||
|
'emails': 0
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i = 0; i < tableData.length; i++) {
|
for (i = 0; i < tableData.length; i++) {
|
||||||
if (tableData[i].id) {
|
if (tableData[i].id) {
|
||||||
var options = {user_id: tableData[i].id, metric: "marks"};
|
var user_link = make_uri("/", {user_id: tableData[i].id, metric: "marks"});
|
||||||
var link = make_uri("/", options);
|
var company_link = make_uri("/", {company: tableData[i].company, metric: "marks"});
|
||||||
tableData[i].link = "<a href=\"" + link + "\">" + tableData[i].name + " (" + tableData[i].id + ")</a>"
|
tableData[i].link = "<a href=\"" + user_link + "\">" + tableData[i].name + "</a>" +
|
||||||
|
" <a href=\"" + company_link + "\">" + "(" + tableData[i].company + ")</a>"
|
||||||
} else {
|
} else {
|
||||||
tableData[i].link = tableData[i].name
|
tableData[i].link = tableData[i].name
|
||||||
}
|
}
|
||||||
@ -48,7 +54,15 @@ Reviews for the last {{ days }} days in {{ module }}
|
|||||||
} else if (tableData[i].core) {
|
} else if (tableData[i].core) {
|
||||||
tableData[i].link += " ✬ <small><i>" + tableData[i].core + "</i></small>";
|
tableData[i].link += " ✬ <small><i>" + tableData[i].core + "</i></small>";
|
||||||
}
|
}
|
||||||
|
if (tableData[i].metric > 0) {
|
||||||
|
summary.reviewers ++;
|
||||||
|
}
|
||||||
|
tableData[i].review_ratio = tableData[i].review + " / " + tableData[i].patch_count;
|
||||||
summary.marks += tableData[i].metric;
|
summary.marks += tableData[i].metric;
|
||||||
|
summary.commits += tableData[i].commit;
|
||||||
|
summary.reviews += tableData[i].review;
|
||||||
|
summary.patch_count += tableData[i].patch_count;
|
||||||
|
summary.emails += tableData[i].email;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (table_id) {
|
if (table_id) {
|
||||||
@ -64,7 +78,7 @@ Reviews for the last {{ days }} days in {{ module }}
|
|||||||
"aaData": tableData,
|
"aaData": tableData,
|
||||||
"aoColumns": tableColumns,
|
"aoColumns": tableColumns,
|
||||||
"aoColumnDefs": [
|
"aoColumnDefs": [
|
||||||
{ "sClass": "center", "aTargets": [2, 3, 4, 5, 6, 7, 8, 9, 10] }
|
{ "sClass": "center", "aTargets": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -94,12 +108,17 @@ Reviews for the last {{ days }} days in {{ module }}
|
|||||||
<div>Total reviewers: <b>${reviewers}</b> (${(marks / reviewers / {{ days }}).toFixed(1) } per reviewer per day)</div>
|
<div>Total reviewers: <b>${reviewers}</b> (${(marks / reviewers / {{ days }}).toFixed(1) } per reviewer per day)</div>
|
||||||
<div>Total reviews by core team: <b>${core_marks}</b> (${(core_marks / {{ days }}).toFixed(1) } per day)</div>
|
<div>Total reviews by core team: <b>${core_marks}</b> (${(core_marks / {{ days }}).toFixed(1) } per day)</div>
|
||||||
<div>Core team size: <b>${core_reviewers}</b> (${(core_marks / core_reviewers / {{ days }}).toFixed(1) } per core per day)</div>
|
<div>Core team size: <b>${core_reviewers}</b> (${(core_marks / core_reviewers / {{ days }}).toFixed(1) } per core per day)</div>
|
||||||
|
<h2>Contribution Summary</h2>
|
||||||
|
<div>On review: <b>${reviews}</b> (${(reviews / {{ days }}).toFixed(1) } per day)</div>
|
||||||
|
<div>Patch sets: <b>${patch_count}</b> (${(patch_count / {{ days }}).toFixed(1) } per day)</div>
|
||||||
|
<div>Commits: <b>${commits}</b> (${(commits / {{ days }}).toFixed(1) } per day)</div>
|
||||||
|
<div>Emails: <b>${emails}</b> (${(emails / {{ days }}).toFixed(1) } per day)</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Reviews for the last {{ days }} days in {{ module }}</h1>
|
<h1>Contribution into {{ module }} for the last {{ days }} days</h1>
|
||||||
|
|
||||||
<table id="review_stats_table">
|
<table id="review_stats_table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -112,9 +131,12 @@ Reviews for the last {{ days }} days in {{ module }}
|
|||||||
<th>+1</th>
|
<th>+1</th>
|
||||||
<th>+2</th>
|
<th>+2</th>
|
||||||
<th>A</th>
|
<th>A</th>
|
||||||
<th>(+/- %)</th>
|
<th>+/-</th>
|
||||||
<th>Disagreements</th>
|
<th>Disagreements</th>
|
||||||
<th>Ratio</th>
|
<th>Ratio</th>
|
||||||
|
<th>On review / patch sets</th>
|
||||||
|
<th>Commits</th>
|
||||||
|
<th>Emails</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -78,8 +78,7 @@ def page_not_found(e):
|
|||||||
# AJAX Handlers ---------
|
# AJAX Handlers ---------
|
||||||
|
|
||||||
def _get_aggregated_stats(records, metric_filter, keys, param_id,
|
def _get_aggregated_stats(records, metric_filter, keys, param_id,
|
||||||
param_title=None, finalize_handler=None,
|
param_title=None, finalize_handler=None):
|
||||||
postprocessing=None):
|
|
||||||
param_title = param_title or param_id
|
param_title = param_title or param_id
|
||||||
result = dict((c, {'metric': 0, 'id': c}) for c in keys)
|
result = dict((c, {'metric': 0, 'id': c}) for c in keys)
|
||||||
for record in records:
|
for record in records:
|
||||||
@ -87,12 +86,11 @@ 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]
|
||||||
|
|
||||||
if not finalize_handler:
|
if not finalize_handler:
|
||||||
finalize_handler = lambda x: x
|
response = [r for r in result.values() if r['metric']]
|
||||||
|
else:
|
||||||
response = [finalize_handler(result[r]) for r in result
|
response = result.values()
|
||||||
if result[r]['metric']]
|
|
||||||
response.sort(key=lambda x: x['metric'], reverse=True)
|
response.sort(key=lambda x: x['metric'], reverse=True)
|
||||||
response = [item for item in map(postprocessing, response) if item]
|
response = [item for item in map(finalize_handler, response) if item]
|
||||||
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
|
||||||
|
|
||||||
@ -122,7 +120,7 @@ def get_modules(records, metric_filter, finalize_handler):
|
|||||||
@app.route('/api/1.0/stats/engineers')
|
@app.route('/api/1.0/stats/engineers')
|
||||||
@decorators.jsonify('stats')
|
@decorators.jsonify('stats')
|
||||||
@decorators.exception_handler()
|
@decorators.exception_handler()
|
||||||
@decorators.record_filter()
|
@decorators.record_filter(ignore='metric')
|
||||||
@decorators.aggregate_filter()
|
@decorators.aggregate_filter()
|
||||||
def get_engineers(records, metric_filter, finalize_handler):
|
def get_engineers(records, metric_filter, finalize_handler):
|
||||||
|
|
||||||
@ -130,8 +128,20 @@ def get_engineers(records, metric_filter, finalize_handler):
|
|||||||
modules_names = parameters.get_parameter({}, 'module', 'modules')
|
modules_names = parameters.get_parameter({}, 'module', 'modules')
|
||||||
modules = vault.resolve_modules(modules_names)
|
modules = vault.resolve_modules(modules_names)
|
||||||
|
|
||||||
def filter_core_users(record):
|
def postprocessing(record):
|
||||||
|
if finalize_handler:
|
||||||
|
record = finalize_handler(record)
|
||||||
user = vault.get_user_from_runtime_storage(record['id'])
|
user = vault.get_user_from_runtime_storage(record['id'])
|
||||||
|
record['company'] = user['companies'][-1]['company_name']
|
||||||
|
record['review'] = record.get('review', 0)
|
||||||
|
record['commit'] = record.get('commit', 0)
|
||||||
|
record['email'] = record.get('email', 0)
|
||||||
|
record['patch_count'] = record.get('patch_count', 0)
|
||||||
|
|
||||||
|
if not (record['metric'] or record['review'] or record['commit'] or
|
||||||
|
record['email'] or record['patch_count']):
|
||||||
|
return
|
||||||
|
|
||||||
is_core = False
|
is_core = False
|
||||||
for (module, branch) in user['core']:
|
for (module, branch) in user['core']:
|
||||||
if module in modules:
|
if module in modules:
|
||||||
@ -147,11 +157,25 @@ def get_engineers(records, metric_filter, finalize_handler):
|
|||||||
record['core'] = is_core
|
record['core'] = is_core
|
||||||
return record
|
return record
|
||||||
|
|
||||||
return _get_aggregated_stats(records, metric_filter,
|
def record_processing(result, record, param_id):
|
||||||
|
record_type = record['record_type']
|
||||||
|
|
||||||
|
result_row = result[record[param_id]]
|
||||||
|
if record_type == 'mark':
|
||||||
|
metric_filter(result, record, param_id)
|
||||||
|
elif record_type == 'commit':
|
||||||
|
result_row['commit'] = result_row.get('commit', 0) + 1
|
||||||
|
elif record_type == 'email':
|
||||||
|
result_row['email'] = result_row.get('email', 0) + 1
|
||||||
|
elif record_type == 'review':
|
||||||
|
result_row['review'] = result_row.get('review', 0) + 1
|
||||||
|
result_row['patch_count'] = (result_row.get('patch_count', 0) +
|
||||||
|
record['patch_count'])
|
||||||
|
|
||||||
|
return _get_aggregated_stats(records, record_processing,
|
||||||
vault.get_memory_storage().get_user_ids(),
|
vault.get_memory_storage().get_user_ids(),
|
||||||
'user_id', 'author_name',
|
'user_id', 'author_name',
|
||||||
finalize_handler=finalize_handler,
|
finalize_handler=postprocessing)
|
||||||
postprocessing=filter_core_users)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/1.0/stats/distinct_engineers')
|
@app.route('/api/1.0/stats/distinct_engineers')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user