Open reviews report is added

* Latest changed reviews
 * Stats since first revision

Part of blueprint module-review-backlog-stats

Change-Id: I1638f42c153c74b4b2f73578c8215a599b949a7a
This commit is contained in:
Ilya Shakhat 2013-10-28 15:19:19 +04:00
parent 674daa654f
commit 37066a0254
4 changed files with 165 additions and 19 deletions

View File

@ -12,6 +12,7 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import operator
import time
@ -48,27 +49,74 @@ def blueprint_summary(module, blueprint_name):
return {'blueprint': bpd, 'activity': activity}
def _get_day(timestamp, time_now):
return int((time_now - timestamp) / 60 / 60 / 24)
def _process_stat(data, key, time_now):
if not data:
return None
data = sorted(data, key=operator.itemgetter(key))
days = _get_day(data[0][key], time_now)
chart_data = [0] * (days + 1)
sum_ages = 0
for review in data:
age = time_now - review[key]
sum_ages += age
review[key + '_age'] = utils.make_age_string(age)
chart_data[_get_day(review[key], time_now)] += 1
return {
'reviews': data,
'average': utils.make_age_string(sum_ages / len(data)),
'max': data[0][key + '_age'],
'chart_data': json.dumps(chart_data),
}
@blueprint.route('/reviews/<module>')
@decorators.templated()
@decorators.exception_handler()
def open_reviews(module):
memory_storage = vault.get_memory_storage()
now = int(time.time())
review_ids = (memory_storage.get_record_ids_by_modules([module]) &
memory_storage.get_record_ids_by_type('review'))
records = []
for review in memory_storage.get_records(review_ids):
if review['status'] != 'NEW':
continue
processed_review = review.copy()
helpers.extend_record(processed_review)
processed_review['age'] = utils.make_age_string(
now - processed_review['date'])
records.append(processed_review)
memory_storage_inst = vault.get_memory_storage()
time_now = int(time.time())
review_marks = {}
reviews = {}
mark_ids = (memory_storage_inst.get_record_ids_by_modules([module]) &
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:
review = memory_storage_inst.get_record_by_primary_key(review_id)
if not review:
continue # todo because we filter jenkins
review_marks[review_id] = mark
reviews[review_id] = review
waiting_on_reviewer = []
total_open = 0
for review_id, mark in review_marks.iteritems():
if reviews[review_id]['open']:
total_open += 1
if mark['value'] in ['1', '2']:
waiting_on_reviewer.append(reviews[review_id])
return {
'module': module,
'oldest': sorted(records, key=operator.itemgetter('date'))[:5]
'total_open': total_open,
'waiting_on_reviewer': len(waiting_on_reviewer),
'waiting_on_submitter': total_open - len(waiting_on_reviewer),
'latest_revision': _process_stat(
waiting_on_reviewer, 'lastUpdated', time_now),
'first_revision': _process_stat(waiting_on_reviewer, 'date', time_now),
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,65 @@
<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' />
<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>
<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 () {
render_bar_chart("latest_revision_chart", [{{ latest_revision.chart_data }}]);
render_bar_chart("first_revision_chart", [{{ first_revision.chart_data }}]);
});
</script>
<style>
.label {
font-weight: bold;
@ -16,11 +75,44 @@
</head>
<body style="margin: 2em;">
<h1>Open Reviews Report</h1>
<h1>Open reviews for {{ module }}</h1>
<h3>Summary:</h3>
<ul>
<li>Total open reviews: {{ total_open }}</li>
<li>Waiting on reviewer: {{ waiting_on_reviewer }}</li>
<li>Waiting on submitter: {{ waiting_on_submitter }}</li>
</ul>
{% if total_open %}
<h3>Stats since last revision</h3>
<ul>
<li>Average wait time: {{ latest_revision.average }}</li>
<li>Max wait time: {{ latest_revision.max }}</li>
</ul>
<div id="latest_revision_chart" style="width: 100%; height: 350px;"></div>
<h3>Longest waiting reviews (since first revision, total age): </h3>
<ol>
{% for item in oldest %}
<li>{{ item.age }} <a href="{{ item.url }}">{{ item.url }}</a> {{ item.subject }}</li>
{% 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>
{% endfor %}
</ol>
</ol>
<h3>Stats since first revision</h3>
<ul>
<li>Average wait time: {{ first_revision.average }}</li>
<li>Max wait time: {{ first_revision.max }}</li>
</ul>
<div id="first_revision_chart" style="width: 100%; height: 350px;"></div>
<ol>
{% for item in first_revision.reviews[:5] %}
<li>{{ item.date_age }} <a href="{{ item.url }}">{{ item.url }}</a> {{ item.subject }} by {{ item.author_name }} ({{ item.company_name }})</li>
{% endfor %}
</ol>
{% endif %}