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:
parent
674daa654f
commit
37066a0254
@ -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),
|
||||
}
|
||||
|
||||
|
||||
|
3
dashboard/static/js/jqplot.barRenderer.min.js
vendored
Normal file
3
dashboard/static/js/jqplot.barRenderer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
dashboard/static/js/jqplot.categoryAxisRenderer.min.js
vendored
Normal file
3
dashboard/static/js/jqplot.categoryAxisRenderer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -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 %}
|
Loading…
x
Reference in New Issue
Block a user