Implements stats on blueprint mentions

Change-Id: I531aa5b723f18f5b65587dd04bd29212100cffaf
This commit is contained in:
Ilya Shakhat 2013-10-14 19:04:49 +04:00
parent bc3d2c6df1
commit 4ad60439a2
6 changed files with 99 additions and 86 deletions

View File

@ -307,3 +307,7 @@ a[href^="https://launchpad"]:after {
.importanceUndecided, .importanceUndecided a { .importanceUndecided, .importanceUndecided a {
color: #999999; color: #999999;
} }
.select2-results {
max-height: 300px;
}

View File

@ -83,12 +83,12 @@ function renderTimeline(options) {
}); });
} }
function renderTableAndChart(url, container_id, table_id, chart_id, link_param, options) { function renderTableAndChart(url, container_id, table_id, chart_id, link_param, table_column_names) {
$(document).ready(function () { $(document).ready(function () {
$.ajax({ $.ajax({
url: make_uri(url, options), url: make_uri(url),
dataType: "json", dataType: "json",
success: function (data) { success: function (data) {
@ -99,7 +99,6 @@ function renderTableAndChart(url, container_id, table_id, chart_id, link_param,
var aggregate = 0; var aggregate = 0;
var index = 1; var index = 1;
var i; var i;
var hasComment = false;
data = data["stats"]; data = data["stats"];
@ -121,18 +120,15 @@ function renderTableAndChart(url, container_id, table_id, chart_id, link_param,
} else { } else {
index++; index++;
} }
var link; if (!data[i].link) {
if (data[i].id) { if (data[i].id) {
link = make_link(data[i].id, data[i].name, link_param); data[i]["link"] = make_link(data[i].id, data[i].name, link_param);
} else { } else {
link = data[i].name data[i]["link"] = data[i].name
}
} }
var rec = {"index": index_label, "link": link, "metric": data[i].metric}; data[i]["index"] = index_label;
if (data[i].comment) { tableData.push(data[i]);
rec["comment"] = data[i].comment;
hasComment = true;
}
tableData.push(rec);
} }
if (i == limit) { if (i == limit) {
@ -141,26 +137,29 @@ function renderTableAndChart(url, container_id, table_id, chart_id, link_param,
chartData.push(["others", aggregate]); chartData.push(["others", aggregate]);
} }
var tableColumns = [ if (!table_column_names) {
{ "mData": "index" }, table_column_names = ["index", "link", "metric"];
{ "mData": "link" }, }
{ "mData": "metric" } var tableColumns = [];
]; var sort_by_column = 0;
if (hasComment) { for (i = 0; i < table_column_names.length; i++) {
tableColumns.push({ "mData": "comment"}) tableColumns.push({"mData": table_column_names[i]})
if (table_column_names[i] == "metric") {
sort_by_column = i;
}
} }
if (table_id) { if (table_id) {
$("#" + table_id).dataTable({ $("#" + table_id).dataTable({
"aLengthMenu": [ "aLengthMenu": [
[25, 50, -1], [10, 25, 50, -1],
[25, 50, "All"] [10, 25, 50, "All"]
], ],
"aaSorting": [ "aaSorting": [
[ 2, "desc" ] [ sort_by_column, "desc" ]
], ],
"sPaginationType": "full_numbers", "sPaginationType": "full_numbers",
"iDisplayLength": 25, "iDisplayLength": 10,
"aaData": tableData, "aaData": tableData,
"aoColumns": tableColumns "aoColumns": tableColumns
}); });

View File

@ -19,10 +19,10 @@
<link rel="icon" href="{{ url_for('static', filename='images/favicon.png') }}" type="image/png"/> <link rel="icon" href="{{ url_for('static', filename='images/favicon.png') }}" type="image/png"/>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/jquery.jqplot.min.css') }}"> <link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/jquery.jqplot.min.css') }}">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/jquery.dataTables.css') }}"> <link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/jquery.dataTables.css') }}">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/select2.css') }}"> <link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/select2.css') }}">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-1.9.1.min.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/jquery-1.9.1.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.dataTables.min.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/jquery.dataTables.min.js') }}"></script>

View File

@ -2,7 +2,7 @@
{% set show_company_breakdown = (not company) and (not user_id) %} {% set show_company_breakdown = (not company) and (not user_id) %}
{% set show_engineer_breakdown = (not user_id) %} {% set show_engineer_breakdown = (not user_id) %}
{% set show_bp_breakdown = (module) and (not user_id) and (metric == 'bpd!') %} {% set show_bp_breakdown = (metric in ['bpd', 'bpc']) %}
{% set show_module_breakdown = (not module) %} {% set show_module_breakdown = (not module) %}
{% set show_user_activity = (user_id) %} {% set show_user_activity = (user_id) %}
{% set show_module_activity = (module) and (not user_id) %} {% set show_module_activity = (module) and (not user_id) %}
@ -12,6 +12,7 @@
{% set show_contribution = (show_user_contribution) or (show_module_contribution) %} {% set show_contribution = (show_user_contribution) or (show_module_contribution) %}
{% set show_user_profile = (user_id) %} {% set show_user_profile = (user_id) %}
{% set show_top_mentors_options = (metric == 'tm_marks') %} {% set show_top_mentors_options = (metric == 'tm_marks') %}
{% set show_review_ratio = (metric in ['marks', 'tm_marks']) %}
{% block scripts %} {% block scripts %}
<script type="text/javascript"> <script type="text/javascript">
@ -22,10 +23,16 @@
renderTableAndChart("/api/1.0/stats/companies", "company_container", "company_table", "company_chart", "company"); renderTableAndChart("/api/1.0/stats/companies", "company_container", "company_table", "company_chart", "company");
{% endif %} {% endif %}
{% if show_engineer_breakdown %} {% if show_engineer_breakdown %}
renderTableAndChart("/api/1.0/stats/engineers", "engineer_container", "engineer_table", "engineer_chart", "user_id", {company: "{{ company|encode }}" }); {% if show_review_ratio %}
renderTableAndChart("/api/1.0/stats/engineers", "engineer_container", "engineer_table", "engineer_chart", "user_id",
["index", "link", "metric", "mark_ratio"]);
{% else %}
renderTableAndChart("/api/1.0/stats/engineers", "engineer_container", "engineer_table", "engineer_chart", "user_id");
{% endif %}
{% endif %} {% endif %}
{% if show_bp_breakdown %} {% if show_bp_breakdown %}
renderTableAndChart("/api/1.0/stats/bpd", "bp_container", "bp_table", "bp_chart", "bpd", {module: "{{ module|encode }}" }); renderTableAndChart("/api/1.0/stats/bp", "bp_container", "bp_table", "bp_chart", "name",
["index", "link", "status", "date", "metric"]);
{% endif %} {% endif %}
{% if show_module_breakdown %} {% if show_module_breakdown %}
renderTableAndChart("/api/1.0/stats/modules", "module_container", "module_table", "module_chart", "module"); renderTableAndChart("/api/1.0/stats/modules", "module_container", "module_table", "module_chart", "module");
@ -135,7 +142,7 @@
</div> </div>
{%/if%} {%/if%}
{%elif ((record_type == "bpd") || (record_type == "bpc")) %} {%elif ((record_type == "bpd") || (record_type == "bpc")) %}
<div style='font-weight: bold;'>${title} (<a href="/report/blueprint/${module}/${name}" class="ext_link">${name}</a>)</div> <div style='font-weight: bold;'>${title} ({%html blueprint_link %})</div>
<div style='white-space: pre-wrap;'>${summary}</div> <div style='white-space: pre-wrap;'>${summary}</div>
<div>Priority: <span class="specpriority${priority}">${priority}</span></div> <div>Priority: <span class="specpriority${priority}">${priority}</span></div>
@ -205,7 +212,7 @@
}); });
</script> </script>
<div class="drop" style="margin-top: 1em;"> <div class="drop" style="margin-top: 1em;">
<label for="review_nth">Review #</label> <label for="review_nth">Aggregate stats for first # reviews</label>
<select id="review_nth" name="review_nth" <select id="review_nth" name="review_nth"
style="min-width: 140px;" style="min-width: 140px;"
data-placeholder="Select review #"> data-placeholder="Select review #">
@ -266,27 +273,6 @@
</div> </div>
{% endif %} {% endif %}
{% if show_bp_breakdown %}
<div id="bp_container">
<h2>Mention # by blueprint</h2>
<div id="bp_chart" style="width: 100%; height: 350px;"></div>
<table id="bp_table" class="display">
<thead>
<tr>
<th>#</th>
<th>Blueprint</th>
<th>Mention #</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="spacer"></div>
</div>
{% endif %}
{% if show_user_profile %} {% if show_user_profile %}
<div id="user_profile_container" style="margin-bottom: 2em;"></div> <div id="user_profile_container" style="margin-bottom: 2em;"></div>
{% endif %} {% endif %}
@ -330,6 +316,29 @@
</div> </div>
{% endif %} {% endif %}
{% if show_bp_breakdown %}
<div id="bp_container">
<h2>Mention # by blueprint</h2>
<div id="bp_chart" style="width: 100%; height: 350px;"></div>
<table id="bp_table" class="display">
<thead>
<tr>
<th>#</th>
<th>Blueprint</th>
<th>Status</th>
<th>Date</th>
<th>Mentions</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="spacer"></div>
</div>
{% endif %}
{% if show_user_contribution %} {% if show_user_contribution %}
<div id="contribution_container"></div> <div id="contribution_container"></div>
{% endif %} {% endif %}

View File

@ -405,7 +405,7 @@ def aggregate_filter():
else: else:
mark_distribution.append('0') mark_distribution.append('0')
new_record['comment'] = ( new_record['mark_ratio'] = (
'|'.join(mark_distribution) + '|'.join(mark_distribution) +
' (%.1f%%)' % ((positive * 100.0) / record['metric'])) ' (%.1f%%)' % ((positive * 100.0) / record['metric']))
return new_record return new_record
@ -685,6 +685,8 @@ def get_activity_json(records):
if 'mention_date' in record: if 'mention_date' in record:
blueprint['mention_date_str'] = format_datetime( blueprint['mention_date_str'] = format_datetime(
record['mention_date']) record['mention_date'])
blueprint['blueprint_link'] = make_blueprint_link(
blueprint['name'], blueprint['module'])
result.append(blueprint) result.append(blueprint)
result.sort(key=lambda x: x['date'], reverse=True) result.sort(key=lambda x: x['date'], reverse=True)
@ -800,35 +802,24 @@ def get_module(module):
flask.abort(404) flask.abort(404)
@app.route('/api/1.0/stats/bpd/<module>') @app.route('/api/1.0/stats/bp')
@jsonify('stats') @jsonify('stats')
@exception_handler() @exception_handler()
def get_bpd(module): @record_filter()
memory_storage_inst = get_vault()['memory_storage'] def get_bpd(records):
module = module.lower()
record_ids = (
set(memory_storage_inst.get_record_ids_by_type('bpd')) &
set(memory_storage_inst.get_record_ids_by_modules([module])))
# param = get_parameter(kwargs, 'release', 'releases', use_default)
# if param:
# if 'all' not in param:
# record_ids &= (
# memory_storage.get_record_ids_by_releases(
# c.lower() for c in param))
result = [] result = []
for record in memory_storage_inst.get_records(record_ids): for record in records:
result.append({ if record['record_type'] in ['bpd', 'bpc']:
'date': record['date'], result.append({
'lifecycle_status': record['lifecycle_status'], 'date': format_date(record['date']),
'metric': record['mention_count'], 'status': record['lifecycle_status'],
'id': record['name'], 'metric': record['mention_count'],
'name': record['name'], 'id': record['name'],
}) 'name': record['name'],
'link': make_blueprint_link(record['name'], record['module'])
})
result.sort(key=lambda x: x['metric']) result.sort(key=lambda x: x['metric'], reverse=True)
return result return result
@ -996,6 +987,10 @@ def format_datetime(timestamp):
timestamp).strftime('%d %b %Y %H:%M:%S') timestamp).strftime('%d %b %Y %H:%M:%S')
def format_date(timestamp):
return datetime.datetime.utcfromtimestamp(timestamp).strftime('%d-%b-%y')
@app.template_filter('launchpadmodule') @app.template_filter('launchpadmodule')
def format_launchpad_module_link(module): def format_launchpad_module_link(module):
return '<a href="https://launchpad.net/%s">%s</a>' % (module, module) return '<a href="https://launchpad.net/%s">%s</a>' % (module, module)
@ -1023,6 +1018,11 @@ def make_link(title, uri=None, options=None):
return '<a href="%(uri)s">%(title)s</a>' % {'uri': uri, 'title': title} return '<a href="%(uri)s">%(title)s</a>' % {'uri': uri, 'title': title}
def make_blueprint_link(name, module):
uri = '/report/blueprint/' + module + '/' + name
return '<a href="%(uri)s">%(title)s</a>' % {'uri': uri, 'title': name}
def make_commit_message(record): def make_commit_message(record):
s = record['message'] s = record['message']
module = record['module'] module = record['module']

View File

@ -386,7 +386,7 @@ class RecordProcessor(object):
'count': 1, 'count': 1,
'date': record['date'] 'date': record['date']
} }
if record['record_type'] in ['bpd', 'bpi']: if record['record_type'] in ['bpd', 'bpc']:
valid_blueprints[record['id']] = { valid_blueprints[record['id']] = {
'primary_key': record['primary_key'], 'primary_key': record['primary_key'],
'count': 0, 'count': 0,
@ -401,12 +401,13 @@ class RecordProcessor(object):
else: else:
users_reviews[launchpad_id] = [review] users_reviews[launchpad_id] = [review]
for bp in valid_blueprints.keys(): for bp_name, bp in valid_blueprints.iteritems():
if bp in mentioned_blueprints: if bp_name in mentioned_blueprints:
valid_blueprints[bp]['count'] = ( bp['count'] = mentioned_blueprints[bp_name]['count']
mentioned_blueprints[bp]['count']) bp['date'] = mentioned_blueprints[bp_name]['date']
valid_blueprints[bp]['date'] = ( else:
mentioned_blueprints[bp]['date']) bp['count'] = 0
bp['date'] = 0
reviews_index = {} reviews_index = {}
for launchpad_id, reviews in users_reviews.iteritems(): for launchpad_id, reviews in users_reviews.iteritems():
@ -441,7 +442,7 @@ class RecordProcessor(object):
need_update = True need_update = True
record['blueprint_id'] = list(valid_bp) record['blueprint_id'] = list(valid_bp)
if record['record_type'] in ['bpd', 'bpi']: if record['record_type'] in ['bpd', 'bpc']:
bp = valid_blueprints[record['id']] bp = valid_blueprints[record['id']]
if ((record.get('mention_count') != bp['count']) or if ((record.get('mention_count') != bp['count']) or
(record.get('mention_date') != bp['date'])): (record.get('mention_date') != bp['date'])):