Implemented summary report on reviews per engineer

Partially implements blueprint review-stats

Change-Id: I011d81a10ad63746a9246a62379c0f6b4c814812
This commit is contained in:
Ilya Shakhat 2013-08-07 20:17:34 +04:00
parent 6779de9dc2
commit 7fcf69efdf
4 changed files with 95 additions and 52 deletions

View File

@ -86,7 +86,7 @@
{% endif %}
<h4>Marks</h4>
<h3>Reviews</h3>
{% for mark in [-2, -1, 0, 1, 2] %}
<div>{{ mark }}: {{ marks[mark] }}</div>
{% endfor %}

View File

@ -114,6 +114,7 @@
var aggregate = 0;
var index = 1;
var i;
var hasComment = false;
for (i = 0; i < data.length; i++) {
if (i < limit - 1) {
@ -133,7 +134,12 @@
} else {
var link = data[i].name
}
tableData.push({"index": index_label, "link": link, "metric": data[i].metric});
var rec = {"index": index_label, "link": link, "metric": data[i].metric};
if (data[i].comment) {
rec["comment"] = data[i].comment;
hasComment = true;
}
tableData.push(rec);
}
if (i == limit) {
@ -142,6 +148,15 @@
chartData.push(["others", aggregate]);
}
var tableColumns = [
{ "mData": "index" },
{ "mData": "link" },
{ "mData": "metric" }
];
if (hasComment) {
tableColumns.push({ "mData": "comment"})
}
$("#" + table_id).dataTable({
"aLengthMenu": [
[25, 50, -1],
@ -153,11 +168,7 @@
"sPaginationType": "full_numbers",
"iDisplayLength": 25,
"aaData": tableData,
"aoColumns": [
{ "mData": "index" },
{ "mData": "link" },
{ "mData": "metric" }
]
"aoColumns": tableColumns
});
var plot = $.jqplot(chart_id, [chartData], {

View File

@ -7,6 +7,7 @@
{% block scripts %}
<script type="text/javascript">
chartAndTableRenderer("/data/companies", "left_list", "left_chart", "/companies/", {module: "{{ module }}" });
chartAndTableRenderer("/data/engineers", "right_list", "right_chart", "/engineers/", {module: "{{ module }}" });
timelineRenderer({module: "{{ module }}" })
</script>
{% endblock %}
@ -34,37 +35,24 @@
{% block right_frame %}
<h2>Recent activity</h2>
<h2>Contribution by engineers</h2>
{% for rec in commits %}
<div style="padding-bottom: 1em;">
<div style='float: left; '>
<img src="{{ rec.author_email|gravatar(size=32) }}">
</div>
<div style="margin-left: 4em;">
<div>
{% if rec.launchpad_id %}
{{ rec.author_name|link('/engineers/' + rec.launchpad_id)|safe }}
{% else %}
{{ rec.author_name }}
{% endif %}
{% if rec.company_name %}
(
{{ rec.company_name|link('/companies/' + rec.company_name)|safe }}
)
{% endif %}
<em>{{ rec.date|datetimeformat }}</em>
</div>
<div id="right_chart" style="width: 100%; height: 350px;"></div>
{% if rec.correction_comment %}
<div style='font-weight: bold; color: red; padding-left: 2em;'>Commit corrected: {{ rec.correction_comment }}</div>
<table id="right_list" class="display">
<thead>
<tr>
<th>#</th>
<th>Engineer</th>
<th>{{ metric_label }}</th>
{% if metric == 'marks' %}
<th>-2|-1|+1|+2 (+/- ratio)</th>
{% endif %}
<div><b>{{ rec.subject }}</b></div>
<div style="white-space: pre-wrap;">{{ rec|commit_message|safe }}</div>
<div><span style="color: green">+ {{ rec.lines_added }}</span>
<span style="color: red">- {{ rec.lines_deleted }}</span></div>
</div>
</div>
{% endfor %}
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="spacer"></div>
{% endblock %}

View File

@ -44,8 +44,7 @@ DEFAULTS = {
METRIC_LABELS = {
'loc': 'Lines of code',
'commits': 'Commits',
'reviews': 'Reviews',
'marks': 'Marks',
'marks': 'Reviews',
}
DEFAULT_RECORDS_LIMIT = 10
@ -277,17 +276,59 @@ def aggregate_filter():
@functools.wraps(f)
def aggregate_filter_decorated_function(*args, **kwargs):
def commit_filter(result, record, param_id):
result[record[param_id]]['metric'] += 1
def loc_filter(result, record, param_id):
result[record[param_id]]['metric'] += record['loc']
def mark_filter(result, record, param_id):
value = record['value']
result_by_param = result[record[param_id]]
result_by_param['metric'] += 1
if value in result_by_param:
result_by_param[value] += 1
else:
result_by_param[value] = 1
def mark_finalize(record):
new_record = {}
for key in ['id', 'metric', 'name']:
new_record[key] = record[key]
positive = 0
mark_distribution = []
for key in ['-2', '-1', '1', '2']:
if key in record:
if key in ['1', '2']:
positive += record[key]
mark_distribution.append(str(record[key]))
else:
mark_distribution.append('0')
new_record['comment'] = (
'|'.join(mark_distribution) +
' (%.1f%%)' % ((positive * 100.0) / record['metric']))
return new_record
metric_param = (flask.request.args.get('metric') or
get_default('metric'))
metric = metric_param.lower()
if metric in ['commits', 'reviews', 'marks']:
metric_filter = lambda r: 1
aggregate_filter = None
if metric == 'commits':
metric_filter = commit_filter
elif metric == 'loc':
metric_filter = lambda r: r['loc']
metric_filter = loc_filter
elif metric == 'marks':
metric_filter = mark_filter
aggregate_filter = mark_finalize
else:
raise Exception('Invalid metric %s' % metric)
kwargs['metric_filter'] = metric_filter
kwargs['finalize_handler'] = aggregate_filter
return f(*args, **kwargs)
return aggregate_filter_decorated_function
@ -471,16 +512,18 @@ def engineer_details(user_id, records):
# AJAX Handlers ---------
def _get_aggregated_stats(records, metric_filter, keys, param_id,
param_title=None):
param_title=None, finalize_handler=None):
param_title = param_title or param_id
result = dict((c, 0) for c in keys)
titles = {}
result = dict((c, {'metric': 0, 'id': c}) for c in keys)
for record in records:
result[record[param_id]] += metric_filter(record)
titles[record[param_id]] = record[param_title]
metric_filter(result, record, param_id)
result[record[param_id]]['name'] = record[param_title]
response = [{'id': r, 'metric': result[r], 'name': titles[r]}
for r in result if result[r]]
if not finalize_handler:
finalize_handler = lambda x: x
response = [finalize_handler(result[r]) for r in result
if result[r]['metric']]
response.sort(key=lambda x: x['metric'], reverse=True)
return response
@ -489,7 +532,7 @@ def _get_aggregated_stats(records, metric_filter, keys, param_id,
@exception_handler()
@record_filter()
@aggregate_filter()
def get_companies(records, metric_filter):
def get_companies(records, metric_filter, finalize_handler):
response = _get_aggregated_stats(records, metric_filter,
get_memory_storage().get_companies(),
'company_name')
@ -500,7 +543,7 @@ def get_companies(records, metric_filter):
@exception_handler()
@record_filter()
@aggregate_filter()
def get_modules(records, metric_filter):
def get_modules(records, metric_filter, finalize_handler):
response = _get_aggregated_stats(records, metric_filter,
get_memory_storage().get_modules(),
'module')
@ -511,10 +554,11 @@ def get_modules(records, metric_filter):
@exception_handler()
@record_filter()
@aggregate_filter()
def get_engineers(records, metric_filter):
def get_engineers(records, metric_filter, finalize_handler):
response = _get_aggregated_stats(records, metric_filter,
get_memory_storage().get_user_ids(),
'user_id', 'author_name')
'user_id', 'author_name',
finalize_handler=finalize_handler)
return json.dumps(response)