Merge "Open reviews report is finished"
This commit is contained in:
commit
06dd4280c9
@ -140,9 +140,9 @@ def aggregate_filter():
|
||||
|
||||
positive = 0
|
||||
mark_distribution = []
|
||||
for key in ['-2', '-1', '1', '2']:
|
||||
for key in [-2, -1, 1, 2]:
|
||||
if key in record:
|
||||
if key in ['1', '2']:
|
||||
if key in [1, 2]:
|
||||
positive += record[key]
|
||||
mark_distribution.append(str(record[key]))
|
||||
else:
|
||||
|
@ -130,7 +130,7 @@ class CachedMemoryStorage(MemoryStorage):
|
||||
def get_original_company_name(self, company_name):
|
||||
normalized = company_name.lower()
|
||||
if normalized not in self.company_name_mapping:
|
||||
raise Exception('Unknown company name %s' % company_name)
|
||||
return normalized
|
||||
return self.company_name_mapping[normalized]
|
||||
|
||||
def get_companies(self):
|
||||
|
@ -83,31 +83,24 @@ def open_reviews(module):
|
||||
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
|
||||
module_id_index = vault.get_vault()['module_id_index']
|
||||
module = module.lower()
|
||||
if module in module_id_index:
|
||||
modules = module_id_index[module]['modules']
|
||||
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
|
||||
modules = [module]
|
||||
|
||||
review_ids = (memory_storage_inst.get_record_ids_by_modules(modules) &
|
||||
memory_storage_inst.get_record_ids_by_type('review'))
|
||||
|
||||
waiting_on_reviewer = []
|
||||
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
|
||||
if mark['value'] in ['1', '2']:
|
||||
waiting_on_reviewer.append(reviews[review_id])
|
||||
if review['value'] in [1, 2]:
|
||||
waiting_on_reviewer.append(review)
|
||||
|
||||
return {
|
||||
'module': module,
|
||||
@ -115,7 +108,7 @@ def open_reviews(module):
|
||||
'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),
|
||||
waiting_on_reviewer, 'updated_on', time_now),
|
||||
'first_revision': _process_stat(waiting_on_reviewer, 'date', time_now),
|
||||
}
|
||||
|
||||
|
@ -159,6 +159,12 @@ div#right_list_wrapper {
|
||||
|
||||
.message {
|
||||
white-space: pre-wrap;
|
||||
margin-top: 0.8em;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
line-height: 135%;
|
||||
}
|
||||
|
||||
a[href^="https://blueprints"]:after {
|
||||
|
@ -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() {
|
||||
var vars = {};
|
||||
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
|
||||
|
@ -318,7 +318,7 @@
|
||||
|
||||
{% if show_module_profile %}
|
||||
<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>
|
||||
{% endif %}
|
||||
|
91
dashboard/templates/reports/base_report.html
Normal file
91
dashboard/templates/reports/base_report.html
Normal 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 “{{ item.subject }}”</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 “{{item.subject}}”</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 “{{item.subject}}”</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>
|
@ -1,21 +1,10 @@
|
||||
<!DOCTYPE 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;">
|
||||
{% extends "reports/base_report.html" %}
|
||||
|
||||
{% block title %}
|
||||
Blueprint {{ blueprint.title }} detailed report
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Blueprint “{{ blueprint.name }}”</h1>
|
||||
|
||||
<div><span class="label">Title:</span> {{ blueprint.title }}</div>
|
||||
@ -42,43 +31,6 @@
|
||||
<div class="message">{{ blueprint.whiteboard }}</div>
|
||||
{% endif %}
|
||||
|
||||
<h2>Activity Log</h2>
|
||||
|
||||
{% if not activity %}
|
||||
<div>No activities related to this blueprint.</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 “{{ item.subject }}”</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 “{{item.subject}}”</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 “{{item.subject}}”</div>
|
||||
{% if item.body %}
|
||||
<div class="message">{{ item.body|safe }}</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</body>
|
||||
</html>
|
||||
{{ show_activity_log(activity) }}
|
||||
{% endblock %}
|
||||
|
@ -1,87 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Open reviews report for {{ module }}</title>
|
||||
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
|
||||
{% extends "reports/base_report.html" %}
|
||||
|
||||
<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 title %}
|
||||
Open reviews report for {{ module }}
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<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>
|
||||
{% endblock %}
|
||||
|
||||
<style>
|
||||
.label {
|
||||
font-weight: bold;
|
||||
line-height: 135%;
|
||||
}
|
||||
.message {
|
||||
margin-top: 1em;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin: 2em;">
|
||||
|
||||
{% block body %}
|
||||
<h1>Open reviews for {{ module }}</h1>
|
||||
|
||||
<h3>Summary:</h3>
|
||||
<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>
|
||||
<li>Waiting on reviewer: {{ waiting_on_reviewer }}</li>
|
||||
</ul>
|
||||
|
||||
{% if total_open %}
|
||||
@ -96,7 +35,7 @@
|
||||
|
||||
<ol>
|
||||
{% 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 %}
|
||||
</ol>
|
||||
|
||||
@ -116,3 +55,4 @@
|
||||
</ol>
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -127,7 +127,7 @@ def init_module_groups(vault):
|
||||
module_group_name = module_group['module_group_name']
|
||||
module_group_id = module_group_name.lower()
|
||||
|
||||
module_id_index[module_group_name] = {
|
||||
module_id_index[module_group_id] = {
|
||||
'group': True,
|
||||
'id': module_group_id,
|
||||
'text': module_group_name,
|
||||
|
@ -170,7 +170,7 @@ def get_contribution_json(records):
|
||||
commit_count += 1
|
||||
loc += record['loc']
|
||||
elif record['record_type'] == 'mark':
|
||||
marks[int(record['value'])] += 1
|
||||
marks[record['value']] += 1
|
||||
elif record['record_type'] == 'email':
|
||||
email_count += 1
|
||||
elif record['record_type'] == 'bpd':
|
||||
|
@ -16,7 +16,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"launchpad_id": "openstack",
|
||||
"launchpad_id": "jenkins",
|
||||
"companies": [
|
||||
{
|
||||
"company_name": "*robots",
|
||||
@ -48,8 +48,8 @@
|
||||
"domains": ["intel.com"]
|
||||
},
|
||||
{
|
||||
"domains": ["openstack.org"],
|
||||
"company_name": "OpenStack Foundation"
|
||||
"domains": ["robots"],
|
||||
"company_name": "*robots"
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -206,6 +206,20 @@ class RecordProcessor(object):
|
||||
review['author_email'] = owner['email'].lower()
|
||||
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)
|
||||
|
||||
yield review
|
||||
@ -220,13 +234,14 @@ class RecordProcessor(object):
|
||||
for approval in patch['approvals']:
|
||||
# copy everything and flatten user data
|
||||
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']
|
||||
|
||||
if 'email' not in reviewer or 'username' not in reviewer:
|
||||
continue # ignore
|
||||
|
||||
mark['record_type'] = 'mark'
|
||||
mark['value'] = int(approval['value'])
|
||||
mark['date'] = approval['grantedOn']
|
||||
mark['primary_key'] = (record['id'] +
|
||||
str(mark['date']) +
|
||||
|
Loading…
x
Reference in New Issue
Block a user