Merge "Open reviews report is finished"

This commit is contained in:
Jenkins 2013-10-29 09:33:02 +00:00 committed by Gerrit Code Review
commit 06dd4280c9
13 changed files with 177 additions and 159 deletions

View File

@ -140,9 +140,9 @@ def aggregate_filter():
positive = 0 positive = 0
mark_distribution = [] mark_distribution = []
for key in ['-2', '-1', '1', '2']: for key in [-2, -1, 1, 2]:
if key in record: if key in record:
if key in ['1', '2']: if key in [1, 2]:
positive += record[key] positive += record[key]
mark_distribution.append(str(record[key])) mark_distribution.append(str(record[key]))
else: else:

View File

@ -130,7 +130,7 @@ class CachedMemoryStorage(MemoryStorage):
def get_original_company_name(self, company_name): def get_original_company_name(self, company_name):
normalized = company_name.lower() normalized = company_name.lower()
if normalized not in self.company_name_mapping: if normalized not in self.company_name_mapping:
raise Exception('Unknown company name %s' % company_name) return normalized
return self.company_name_mapping[normalized] return self.company_name_mapping[normalized]
def get_companies(self): def get_companies(self):

View File

@ -83,31 +83,24 @@ def open_reviews(module):
memory_storage_inst = vault.get_memory_storage() memory_storage_inst = vault.get_memory_storage()
time_now = int(time.time()) time_now = int(time.time())
review_marks = {} module_id_index = vault.get_vault()['module_id_index']
reviews = {} module = module.lower()
if module in module_id_index:
mark_ids = (memory_storage_inst.get_record_ids_by_modules([module]) & modules = module_id_index[module]['modules']
memory_storage_inst.get_record_ids_by_type('mark'))
for mark in memory_storage_inst.get_records(mark_ids):
review_id = mark['review_id']
if review_id in review_marks:
if mark['date'] > review_marks[review_id]['date']:
review_marks[review_id] = mark
else: else:
review = memory_storage_inst.get_record_by_primary_key(review_id) modules = [module]
if not review:
continue # todo because we filter jenkins review_ids = (memory_storage_inst.get_record_ids_by_modules(modules) &
review_marks[review_id] = mark memory_storage_inst.get_record_ids_by_type('review'))
reviews[review_id] = review
waiting_on_reviewer = [] waiting_on_reviewer = []
total_open = 0 total_open = 0
for review_id, mark in review_marks.iteritems():
if reviews[review_id]['open']: for review in memory_storage_inst.get_records(review_ids):
if review['status'] == 'NEW':
total_open += 1 total_open += 1
if mark['value'] in ['1', '2']: if review['value'] in [1, 2]:
waiting_on_reviewer.append(reviews[review_id]) waiting_on_reviewer.append(review)
return { return {
'module': module, 'module': module,
@ -115,7 +108,7 @@ def open_reviews(module):
'waiting_on_reviewer': len(waiting_on_reviewer), 'waiting_on_reviewer': len(waiting_on_reviewer),
'waiting_on_submitter': total_open - len(waiting_on_reviewer), 'waiting_on_submitter': total_open - len(waiting_on_reviewer),
'latest_revision': _process_stat( 'latest_revision': _process_stat(
waiting_on_reviewer, 'lastUpdated', time_now), waiting_on_reviewer, 'updated_on', time_now),
'first_revision': _process_stat(waiting_on_reviewer, 'date', time_now), 'first_revision': _process_stat(waiting_on_reviewer, 'date', time_now),
} }

View File

@ -159,6 +159,12 @@ div#right_list_wrapper {
.message { .message {
white-space: pre-wrap; white-space: pre-wrap;
margin-top: 0.8em;
}
.label {
font-weight: bold;
line-height: 135%;
} }
a[href^="https://blueprints"]:after { a[href^="https://blueprints"]:after {

View File

@ -181,6 +181,27 @@ function renderTableAndChart(url, container_id, table_id, chart_id, link_param,
}); });
} }
function render_bar_chart(chart_id, chart_data) {
$.jqplot(chart_id, chart_data, {
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
barMargin: 1
},
pointLabels: {show: true}
},
axes: {
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
label: "Age"
},
yaxis: {
label: "Count"
}
}
});
}
function getUrlVars() { function getUrlVars() {
var vars = {}; var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {

View File

@ -318,7 +318,7 @@
{% if show_module_profile %} {% if show_module_profile %}
<h2>Module {{ module }}</h2> <h2>Module {{ module }}</h2>
<div><a href="/report/reviews/{{ module }}">Open reviews report</a></div> <div><a href="/report/reviews/{{ module }}" target="_blank">Open reviews report</a></div>
<div id="contribution_container"></div> <div id="contribution_container"></div>
{% endif %} {% endif %}

View File

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
<link href='http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic&subset=latin,cyrillic' rel='stylesheet' type='text/css' />
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Caption&subset=latin,cyrillic' rel='stylesheet' type='text/css' />
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700&subset=latin,cyrillic' rel='stylesheet' type='text/css' />
<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/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/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.dataTables.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.jqplot.min.js') }}"></script>
<!--[if lt IE 9]><script type="text/javascript" src="{{ url_for('static', filename='js/excanvas.min.js') }}"></script><![endif]-->
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.json2.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.pieRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.barRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.categoryAxisRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.dateAxisRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.canvasTextRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.canvasAxisTickRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.cursor.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.highlighter.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/select2.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.tmpl.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/stackalytics-ui.js') }}"></script>
{% block head %}{% endblock %}
</head>
<body style="margin: 2em;">
{% macro show_activity_log(activity) -%}
<h2>Activity Log</h2>
{% if not activity %}
<div>No activity.</div>
{% endif %}
{% for item in activity %}
<div style="margin-bottom: 1em;">
<div style='float: left; '><img src="{{ item.author_email | gravatar(size=64) }}">
</div>
<div style="margin-left: 80px;">
<div style="font-weight: bold;">{{ item.date_str}}</div>
<div style="font-weight: bold;">{{ item.author_name }} ({{ item.company_name }})</div>
{% if item.record_type == "commit" %}
<div style='font-weight: bold;'>Commit &ldquo;{{ item.subject }}&rdquo;</div>
<div class="message">{{ item.message | safe }}</div>
<div><span style="color: green">+<span>{{ item.lines_added }}</span></span>
<span style="color: blue">- <span>{{ item.lines_deleted }}</span></span>
</div>
{% if item.correction_comment %}
<div style='font-weight: bold; color: red;'>Commit corrected:
<span>{{ item.correction_comment }}</span></div>
{% endif %}
{% elif item.record_type == "mark" %}
<div style='font-weight: bold;'>Review &ldquo;{{item.subject}}&rdquo;</div>
<div>Patch submitted by {{ parent_author_link }}</div>
<div>Change Id: <a href="{{item.url}}">{{item.review_id}}</a></div>
<div style="color: {% if item.value > 0 %} green {% else %} blue {% endif %}">
{{item.description}}: <span class="review_mark">{{item.value}}</span></div>
{% elif item.record_type == "email" %}
<div style='font-weight: bold;'>Email &ldquo;{{item.subject}}&rdquo;</div>
{% if item.body %}
<div class="message">{{ item.body|safe }}</div>
{% endif %}
{% endif %}
</div>
</div>
{% endfor %}
{%- endmacro %}
<div id="analytics_header" style="padding-bottom: 1em; border-bottom: 1px solid darkgrey;">
<span id="logo"><a href="/">Stackalytics</a></span>
<span id="slogan">| community heartbeat</span>
</div>
{% block body %}{% endblock %}
</body>
</html>

View File

@ -1,21 +1,10 @@
<!DOCTYPE html> {% extends "reports/base_report.html" %}
<html>
<head>
<title>{{ blueprint.title }}</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
<style>
.label {
font-weight: bold;
line-height: 135%;
}
.message {
margin-top: 1em;
white-space: pre-wrap;
}
</style>
</head>
<body style="margin: 2em;">
{% block title %}
Blueprint {{ blueprint.title }} detailed report
{% endblock %}
{% block body %}
<h1>Blueprint &ldquo;{{ blueprint.name }}&rdquo;</h1> <h1>Blueprint &ldquo;{{ blueprint.name }}&rdquo;</h1>
<div><span class="label">Title:</span> {{ blueprint.title }}</div> <div><span class="label">Title:</span> {{ blueprint.title }}</div>
@ -42,43 +31,6 @@
<div class="message">{{ blueprint.whiteboard }}</div> <div class="message">{{ blueprint.whiteboard }}</div>
{% endif %} {% endif %}
<h2>Activity Log</h2>
{% if not activity %} {{ show_activity_log(activity) }}
<div>No activities related to this blueprint.</div> {% endblock %}
{% endif %}
{% for item in activity %}
<div style="margin-bottom: 1em;">
<div style='float: left; '><img src="{{ item.author_email | gravatar(size=64) }}">
</div>
<div style="margin-left: 80px;">
<div style="font-weight: bold;">{{ item.date_str}}</div>
<div style="font-weight: bold;">{{ item.author_name }} ({{ item.company_name }})</div>
{% if item.record_type == "commit" %}
<div style='font-weight: bold;'>Commit &ldquo;{{ item.subject }}&rdquo;</div>
<div class="message">{{ item.message | safe }}</div>
<div><span style="color: green">+<span>{{ item.lines_added }}</span></span>
<span style="color: blue">- <span>{{ item.lines_deleted }}</span></span>
</div>
{% if item.correction_comment %}
<div style='font-weight: bold; color: red;'>Commit corrected:
<span>{{ item.correction_comment }}</span></div>
{% endif %}
{% elif item.record_type == "mark" %}
<div style='font-weight: bold;'>Review &ldquo;{{item.subject}}&rdquo;</div>
<div>Patch submitted by {{ parent_author_link }}</div>
<div>Change Id: <a href="{{item.url}}">{{item.review_id}}</a></div>
<div style="color: {% if item.value > 0 %} green {% else %} blue {% endif %}">
{{item.description}}: <span class="review_mark">{{item.value}}</span></div>
{% elif item.record_type == "email" %}
<div style='font-weight: bold;'>Email &ldquo;{{item.subject}}&rdquo;</div>
{% if item.body %}
<div class="message">{{ item.body|safe }}</div>
{% endif %}
{% endif %}
</div>
</div>
{% endfor %}
</body>
</html>

View File

@ -1,87 +1,26 @@
<!DOCTYPE html> {% extends "reports/base_report.html" %}
<html>
<head>
<title>Open reviews report for {{ module }}</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
<link href='http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic&subset=latin,cyrillic' rel='stylesheet' type='text/css' /> {% block title %}
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Caption&subset=latin,cyrillic' rel='stylesheet' type='text/css' /> Open reviews report for {{ module }}
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700&subset=latin,cyrillic' rel='stylesheet' type='text/css' /> {% endblock %}
<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/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/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.dataTables.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.jqplot.min.js') }}"></script>
<!--[if lt IE 9]><script type="text/javascript" src="{{ url_for('static', filename='js/excanvas.min.js') }}"></script><![endif]-->
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.json2.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.pieRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.barRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.categoryAxisRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.dateAxisRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.canvasTextRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.canvasAxisTickRenderer.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.cursor.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jqplot.highlighter.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/select2.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.tmpl.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/stackalytics-ui.js') }}"></script>
{% block head %}
<script type="text/javascript"> <script type="text/javascript">
function render_bar_chart(chart_id, chart_data) {
$.jqplot(chart_id, chart_data, {
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
barMargin: 1
},
pointLabels: {show: true}
},
axes: {
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
label: "Age"
},
yaxis: {
label: "Count"
}
}
});
}
$(document).ready(function () { $(document).ready(function () {
render_bar_chart("latest_revision_chart", [{{ latest_revision.chart_data }}]); render_bar_chart("latest_revision_chart", [{{ latest_revision.chart_data }}]);
render_bar_chart("first_revision_chart", [{{ first_revision.chart_data }}]); render_bar_chart("first_revision_chart", [{{ first_revision.chart_data }}]);
}); });
</script> </script>
{% endblock %}
<style> {% block body %}
.label {
font-weight: bold;
line-height: 135%;
}
.message {
margin-top: 1em;
white-space: pre-wrap;
}
</style>
</head>
<body style="margin: 2em;">
<h1>Open reviews for {{ module }}</h1> <h1>Open reviews for {{ module }}</h1>
<h3>Summary:</h3> <h3>Summary</h3>
<ul> <ul>
<li>Total open reviews: {{ total_open }}</li> <li>Total open reviews: {{ total_open }}</li>
<li>Waiting on reviewer: {{ waiting_on_reviewer }}</li>
<li>Waiting on submitter: {{ waiting_on_submitter }}</li> <li>Waiting on submitter: {{ waiting_on_submitter }}</li>
<li>Waiting on reviewer: {{ waiting_on_reviewer }}</li>
</ul> </ul>
{% if total_open %} {% if total_open %}
@ -96,7 +35,7 @@
<ol> <ol>
{% for item in latest_revision.reviews[:5] %} {% for item in latest_revision.reviews[:5] %}
<li>{{ item.lastUpdated_age }} <a href="{{ item.url }}">{{ item.url }}</a> {{ item.subject }} by {{ item.author_name }} ({{ item.company_name }})</li> <li>{{ item.updated_on_age }} <a href="{{ item.url }}">{{ item.url }}</a> {{ item.subject }} by {{ item.author_name }} ({{ item.company_name }})</li>
{% endfor %} {% endfor %}
</ol> </ol>
@ -116,3 +55,4 @@
</ol> </ol>
{% endif %} {% endif %}
{% endblock %}

View File

@ -127,7 +127,7 @@ def init_module_groups(vault):
module_group_name = module_group['module_group_name'] module_group_name = module_group['module_group_name']
module_group_id = module_group_name.lower() module_group_id = module_group_name.lower()
module_id_index[module_group_name] = { module_id_index[module_group_id] = {
'group': True, 'group': True,
'id': module_group_id, 'id': module_group_id,
'text': module_group_name, 'text': module_group_name,

View File

@ -170,7 +170,7 @@ def get_contribution_json(records):
commit_count += 1 commit_count += 1
loc += record['loc'] loc += record['loc']
elif record['record_type'] == 'mark': elif record['record_type'] == 'mark':
marks[int(record['value'])] += 1 marks[record['value']] += 1
elif record['record_type'] == 'email': elif record['record_type'] == 'email':
email_count += 1 email_count += 1
elif record['record_type'] == 'bpd': elif record['record_type'] == 'bpd':

View File

@ -16,7 +16,7 @@
] ]
}, },
{ {
"launchpad_id": "openstack", "launchpad_id": "jenkins",
"companies": [ "companies": [
{ {
"company_name": "*robots", "company_name": "*robots",
@ -48,8 +48,8 @@
"domains": ["intel.com"] "domains": ["intel.com"]
}, },
{ {
"domains": ["openstack.org"], "domains": ["robots"],
"company_name": "OpenStack Foundation" "company_name": "*robots"
} }
], ],

View File

@ -206,6 +206,20 @@ class RecordProcessor(object):
review['author_email'] = owner['email'].lower() review['author_email'] = owner['email'].lower()
review['date'] = record['createdOn'] review['date'] = record['createdOn']
patch_sets = record.get('patchSets', [])
review['updated_on'] = review['date']
if patch_sets:
patch = patch_sets[-1]
if 'approvals' in patch:
review['value'] = min([int(p['value'])
for p in patch['approvals']])
review['updated_on'] = patch['approvals'][0]['grantedOn']
else:
review['updated_on'] = patch['createdOn']
if 'value' not in review:
review['value'] = 0
self._update_record_and_user(review) self._update_record_and_user(review)
yield review yield review
@ -220,13 +234,14 @@ class RecordProcessor(object):
for approval in patch['approvals']: for approval in patch['approvals']:
# copy everything and flatten user data # copy everything and flatten user data
mark = dict([(k, v) for k, v in approval.iteritems() mark = dict([(k, v) for k, v in approval.iteritems()
if k not in ['by', 'grantedOn']]) if k not in ['by', 'grantedOn', 'value']])
reviewer = approval['by'] reviewer = approval['by']
if 'email' not in reviewer or 'username' not in reviewer: if 'email' not in reviewer or 'username' not in reviewer:
continue # ignore continue # ignore
mark['record_type'] = 'mark' mark['record_type'] = 'mark'
mark['value'] = int(approval['value'])
mark['date'] = approval['grantedOn'] mark['date'] = approval['grantedOn']
mark['primary_key'] = (record['id'] + mark['primary_key'] = (record['id'] +
str(mark['date']) + str(mark['date']) +