Commit 4979fd85 authored by Weblate's avatar Weblate

Merge remote-tracking branch 'origin/master'

parents 3fcc11ed 40b7dddf
......@@ -40,11 +40,13 @@
{% endif %}
<h4>{% trans "Activity in last 30 days" %}</h4>
<img src="{% url 'monthly_user_activity' user=page_user.username %}">
<span class="hidden" id="load-activity" data-yearly="{% url 'json_yearly_activity' user=page_user.username %}" data-monthly="{% url 'json_monthly_activity' user=page_user.username %}"></span>
<h4>{% trans "Activity in last year" %}</h4>
<img src="{% url 'yearly_user_activity' user=page_user.username %}">
<h3>{% trans "Activity in last 30 days" %}</h3>
<div id="activity-month" class="ct-chart ct-double-octave"></div>
<h3>{% trans "Activity in last year" %}</h3>
<div id="activity-year" class="ct-chart ct-double-octave"></div>
</div>
</div>
......
{% load i18n %}
<div class="tab-pane" id="activity">
<div class="panel panel-primary">
<div class="panel-heading">
<h4 class="panel-title">
{% trans "Activity" %}
<i id="activity-loading" class="fa fa-spinner fa-spin pull-right flip"></i>
</h4>
</div>
<div class="panel-body">
<h3>{% trans "Activity in last 30 days" %}</h3>
<div id="activity-month" class="ct-chart ct-double-octave"></div>
<h3>{% trans "Activity in last year" %}</h3>
<div id="activity-year" class="ct-chart ct-double-octave"></div>
</div>
</div>
</div>
......@@ -30,7 +30,7 @@
{% endif %}
<li><a href="#search" data-toggle="tab">{% trans "Search" %}</a></li>
<li><a href="#history" data-toggle="tab">{% trans "History" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-href="{% url 'view_activity' %}">{% trans "Activity" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-load="activity" data-yearly="{% url 'json_yearly_activity' %}" data-monthly="{% url 'json_monthly_activity' %}">{% trans "Activity" %}</a></li>
<li><a href="#stats" data-toggle="tab">{% trans "Statistics" %}</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
......@@ -108,15 +108,7 @@
</div>
<div class="tab-pane" id="activity">
<div class="panel panel-primary">
<div class="panel-heading"><h4 class="panel-title">{% trans "Activity" %}</h4></div>
<div class="panel-body">
<p>{% trans "Loading…" %}</p>
</div>
</div>
</div>
{% include "activity-tab.html" %}
<div class="tab-pane" id="stats">
<div class="row">
......
{% load i18n %}
<h3>{% trans "Activity in last 30 days" %}</h3>
<img src="{{ monthly_url }}">
<h3>{% trans "Activity in last year" %}</h3>
<img src="{{ yearly_url }}">
......@@ -12,7 +12,7 @@
<ul class="nav nav-pills">
<li class="active"><a href="#overview" data-toggle="tab">{% trans "Overview" %}</a></li>
<li><a href="#history" data-toggle="tab">{% trans "History" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-href="{% url 'view_language_activity' lang=object.code %}">{% trans "Activity" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-load="activity" data-yearly="{% url 'json_yearly_activity' lang=object.code %}" data-monthly="{% url 'json_monthly_activity' lang=object.code %}">{% trans "Activity" %}</a></li>
<li><a href="#dicts" data-toggle="tab">{% trans "Glossaries" %}</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
......@@ -41,14 +41,7 @@
{% include "last-changes.html" %}
</div>
<div class="tab-pane" id="activity">
<div class="panel panel-primary">
<div class="panel-heading"><h4 class="panel-title">{% trans "Activity" %}</h4></div>
<div class="panel-body">
<p>{% trans "Loading…" %}</p>
</div>
</div>
</div>
{% include "activity-tab.html" %}
<div class="tab-pane" id="dicts">
<div class="panel panel-primary">
......
......@@ -8,6 +8,7 @@
<link rel="stylesheet" type="text/css" href="{% get_media_prefix %}font-awesome/css/font-awesome.min.css" />
<link rel="stylesheet" type="text/css" href="{% get_media_prefix %}font-linux/font-linux.css" />
<link rel="stylesheet" type="text/css" href="{% get_media_prefix %}bootstrap/css/datepicker3.css" />
<link rel="stylesheet" type="text/css" href="{% get_media_prefix %}chartist.min.css" />
<link rel="stylesheet" type="text/css" href="{% get_media_prefix %}style-bootstrap.css" />
<script src="{% get_media_prefix %}js/jquery-2.1.1.min.js" type="text/javascript"></script>
<script src="{% get_media_prefix %}js/jquery.cookie.js" type="text/javascript"></script>
......@@ -15,6 +16,7 @@
<script src="{% get_media_prefix %}js/jquery.sortElements.js" type="text/javascript"></script>
<script src="{% get_media_prefix %}js/mousetrap.min.js" type="text/javascript"></script>
<script src="{% get_media_prefix %}js/mousetrap-global-bind.min.js" type="text/javascript"></script>
<script src="{% get_media_prefix %}js/chartist.min.js" type="text/javascript"></script>
<script src="{% get_media_prefix %}bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="{% get_media_prefix %}loader-bootstrap.js" type="text/javascript"></script>
<script src="{% url 'js-catalog' %}" type="text/javascript"></script>
......
......@@ -15,7 +15,7 @@
<ul class="nav nav-pills">
<li class="active"><a href="#overview" data-toggle="tab">{% trans "Overview" %}</a></li>
<li><a href="#history" data-toggle="tab">{% trans "History" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-href="{% url 'view_activity_project' project=object.slug %}">{% trans "Activity" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-load="activity" data-yearly="{% url 'json_yearly_activity' project=object.slug %}" data-monthly="{% url 'json_monthly_activity' project=object.slug %}">{% trans "Activity" %}</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{% trans "Tools" %} <span class="caret"></span>
......@@ -126,15 +126,7 @@
{% include "last-changes.html" %}
</div>
<div class="tab-pane" id="activity">
<div class="panel panel-primary">
<div class="panel-heading"><h4 class="panel-title">{% trans "Activity" %}</h4></div>
<div class="panel-body">
<p>{% trans "Loading…" %}</p>
</div>
</div>
</div>
{% include "activity-tab.html" %}
{% if perms.trans.commit_translation or perms.trans.update_translation %}
<div class="tab-pane" id="repository">
......
......@@ -17,7 +17,7 @@
<ul class="nav nav-pills">
<li class="active"><a href="#overview" data-toggle="tab">{% trans "Overview" %}</a></li>
<li><a href="#history" data-toggle="tab">{% trans "History" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-href="{% url 'view_activity_subproject' project=object.project.slug subproject=object.slug %}">{% trans "Activity" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-load="activity" data-yearly="{% url 'json_yearly_activity' project=object.project.slug subproject=object.slug %}" data-monthly="{% url 'json_monthly_activity' project=object.project.slug subproject=object.slug %}">{% trans "Activity" %}</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{% trans "Tools" %} <span class="caret"></span>
......@@ -113,15 +113,7 @@
{% include "last-changes.html" %}
</div>
<div class="tab-pane" id="activity">
<div class="panel panel-primary">
<div class="panel-heading"><h4 class="panel-title">{% trans "Activity" %}</h4></div>
<div class="panel-body">
<p>{% trans "Loading…" %}</p>
</div>
</div>
</div>
{% include "activity-tab.html" %}
{% if perms.trans.commit_translation or perms.trans.update_translation %}
<div class="tab-pane" id="repository">
......
......@@ -20,7 +20,7 @@
<li class="active"><a href="#overview" data-toggle="tab">{% trans "Overview" %}</a></li>
<li><a href="#search" data-toggle="tab">{% trans "Search" %}</a></li>
<li><a href="#history" data-toggle="tab">{% trans "History" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-href="{% url 'view_activity_translation' project=object.subproject.project.slug subproject=object.subproject.slug lang=object.language.code %}">{% trans "Activity" %}</a></li>
<li><a href="#activity" data-toggle="tab" data-load="activity" data-yearly="{% url 'json_yearly_activity' project=object.subproject.project.slug subproject=object.subproject.slug lang=object.language.code %}" data-monthly="{% url 'json_monthly_activity' project=object.subproject.project.slug subproject=object.subproject.slug lang=object.language.code %}">{% trans "Activity" %}</a></li>
<li><a href="#stats" data-toggle="tab">{% trans "Statistics" %}</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
......@@ -341,15 +341,7 @@
</div>
<div class="tab-pane" id="activity">
<div class="panel panel-primary">
<div class="panel-heading"><h4 class="panel-title">{% trans "Activity" %}</h4></div>
<div class="panel-body">
<p>{% trans "Loading…" %}</p>
</div>
</div>
</div>
{% include "activity-tab.html" %}
</div>
......
/* Chartist.js 0.5.0
* Copyright © 2014 Gion Kunz
* Free to use under the WTFPL license.
* http://www.wtfpl.net/
*/
.ct-chart .ct-label,.ct-chart .ct-label.ct-horizontal{display:block;width:100%;height:100%;fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;text-align:left}.ct-chart .ct-label.ct-vertical{display:block;width:100%;height:100%;fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;text-align:right}.ct-chart .ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-chart .ct-point{stroke-width:10px;stroke-linecap:round}.ct-chart .ct-line{fill:none;stroke-width:4px}.ct-chart .ct-area{stroke:none;fill-opacity:.1}.ct-chart .ct-bar{fill:none;stroke-width:10px}.ct-chart .ct-slice.ct-donut{fill:none;stroke-width:60px}.ct-chart .ct-series.ct-series-a .ct-bar,.ct-chart .ct-series.ct-series-a .ct-line,.ct-chart .ct-series.ct-series-a .ct-point,.ct-chart .ct-series.ct-series-a .ct-slice.ct-donut{stroke:#d70206}.ct-chart .ct-series.ct-series-a .ct-area,.ct-chart .ct-series.ct-series-a .ct-slice:not(.ct-donut){fill:#d70206}.ct-chart .ct-series.ct-series-b .ct-bar,.ct-chart .ct-series.ct-series-b .ct-line,.ct-chart .ct-series.ct-series-b .ct-point,.ct-chart .ct-series.ct-series-b .ct-slice.ct-donut{stroke:#F05B4F}.ct-chart .ct-series.ct-series-b .ct-area,.ct-chart .ct-series.ct-series-b .ct-slice:not(.ct-donut){fill:#F05B4F}.ct-chart .ct-series.ct-series-c .ct-bar,.ct-chart .ct-series.ct-series-c .ct-line,.ct-chart .ct-series.ct-series-c .ct-point,.ct-chart .ct-series.ct-series-c .ct-slice.ct-donut{stroke:#F4C63D}.ct-chart .ct-series.ct-series-c .ct-area,.ct-chart .ct-series.ct-series-c .ct-slice:not(.ct-donut){fill:#F4C63D}.ct-chart .ct-series.ct-series-d .ct-bar,.ct-chart .ct-series.ct-series-d .ct-line,.ct-chart .ct-series.ct-series-d .ct-point,.ct-chart .ct-series.ct-series-d .ct-slice.ct-donut{stroke:#453D3F}.ct-chart .ct-series.ct-series-d .ct-area,.ct-chart .ct-series.ct-series-d .ct-slice:not(.ct-donut){fill:#453D3F}.ct-chart.ct-square{display:block;position:relative;width:100%}.ct-chart.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-chart.ct-square:after{content:"";display:table;clear:both}.ct-chart.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-minor-second{display:block;position:relative;width:100%}.ct-chart.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-chart.ct-minor-second:after{content:"";display:table;clear:both}.ct-chart.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-major-second{display:block;position:relative;width:100%}.ct-chart.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-chart.ct-major-second:after{content:"";display:table;clear:both}.ct-chart.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-minor-third{display:block;position:relative;width:100%}.ct-chart.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-chart.ct-minor-third:after{content:"";display:table;clear:both}.ct-chart.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-major-third{display:block;position:relative;width:100%}.ct-chart.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-chart.ct-major-third:after{content:"";display:table;clear:both}.ct-chart.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-chart.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-chart.ct-perfect-fourth:after{content:"";display:table;clear:both}.ct-chart.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-chart.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-chart.ct-perfect-fifth:after{content:"";display:table;clear:both}.ct-chart.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-minor-sixth{display:block;position:relative;width:100%}.ct-chart.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-chart.ct-minor-sixth:after{content:"";display:table;clear:both}.ct-chart.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-golden-section{display:block;position:relative;width:100%}.ct-chart.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-chart.ct-golden-section:after{content:"";display:table;clear:both}.ct-chart.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-major-sixth{display:block;position:relative;width:100%}.ct-chart.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-chart.ct-major-sixth:after{content:"";display:table;clear:both}.ct-chart.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-minor-seventh{display:block;position:relative;width:100%}.ct-chart.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-chart.ct-minor-seventh:after{content:"";display:table;clear:both}.ct-chart.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-major-seventh{display:block;position:relative;width:100%}.ct-chart.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-chart.ct-major-seventh:after{content:"";display:table;clear:both}.ct-chart.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-octave{display:block;position:relative;width:100%}.ct-chart.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-chart.ct-octave:after{content:"";display:table;clear:both}.ct-chart.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-major-tenth{display:block;position:relative;width:100%}.ct-chart.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-chart.ct-major-tenth:after{content:"";display:table;clear:both}.ct-chart.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-major-eleventh{display:block;position:relative;width:100%}.ct-chart.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-chart.ct-major-eleventh:after{content:"";display:table;clear:both}.ct-chart.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-major-twelfth{display:block;position:relative;width:100%}.ct-chart.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-chart.ct-major-twelfth:after{content:"";display:table;clear:both}.ct-chart.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-chart.ct-double-octave{display:block;position:relative;width:100%}.ct-chart.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-chart.ct-double-octave:after{content:"";display:table;clear:both}.ct-chart.ct-double-octave>svg{display:block;position:absolute;top:0;left:0}
\ No newline at end of file
This diff is collapsed.
var loading = 0;
var mt_loaded = false;
var activity_loaded = false;
function inc_loading() {
function inc_loading(sel) {
if (loading === 0) {
$('#mt-loading').show();
$(sel).show();
}
loading++;
}
function dec_loading() {
function dec_loading(sel) {
loading--;
if (loading === 0) {
$('#mt-loading').hide();
$(sel).hide();
}
}
......@@ -42,6 +43,33 @@ jQuery.fn.extend({
}
});
function load_activity(element) {
if (activity_loaded) {
return;
}
activity_loaded = true;
inc_loading('#activity-loading');
$.ajax({
url: element.data('monthly'),
success: function(data) {
new Chartist.Bar('#activity-month', data);
dec_loading('#activity-loading');
},
dataType: 'json'
});
inc_loading('#activity-loading');
$.ajax({
url: element.data('yearly'),
success: function(data) {
new Chartist.Bar('#activity-year', data);
dec_loading('#activity-loading');
},
dataType: 'json'
});
}
function init_editor(editors) {
/* Autosizing */
$('.translation-editor').autosize();
......@@ -92,7 +120,7 @@ function text_change(e) {
}
function process_machine_translation(data, textStatus, jqXHR) {
dec_loading();
dec_loading('#mt-loading');
if (data.responseStatus == 200) {
data.translations.forEach(function (el, idx, ar) {
var new_row = $('<tr/>').data('quality', el.quality);
......@@ -129,16 +157,16 @@ function process_machine_translation(data, textStatus, jqXHR) {
}
function failed_machine_translation(jqXHR, textStatus, errorThrown) {
dec_loading();
dec_loading('#mt-loading');
$('#mt-errors').append(
$('<li>' + gettext('The request for machine translation has failed:') + ' ' + textStatus + '</li>')
);
}
function load_mt_translations(data, textStatus, jqXHR) {
dec_loading();
dec_loading('#mt-loading');
data.forEach(function (el, idx, ar) {
inc_loading();
inc_loading('#mt-loading');
$.ajax({
url: $('#js-translate').attr('href') + '?service=' + el,
success: process_machine_translation,
......@@ -287,13 +315,24 @@ $(function () {
);
});
/* Activity charts on tabs */
$(document).on('show.bs.tab', '[data-load="activity"]', function (e) {
load_activity($(this));
});
/* Automatic loading of activity charts on page load */
var auto_load_activity = $('#load-activity');
if (auto_load_activity.length > 0) {
load_activity(auto_load_activity);
}
/* Machine translation */
$(document).on('show.bs.tab', '[data-load="mt"]', function (e) {
if (mt_loaded) {
return;
}
mt_loaded = true;
inc_loading();
inc_loading('#mt-loading');
$.ajax({
url: $('#js-mt-services').attr('href'),
success: load_mt_translations,
......
......@@ -225,3 +225,7 @@ img.engage-icon {
#columns-menu label {
font-weight: normal;
}
.ct-chart .ct-series.ct-series-a .ct-bar {
stroke: #337AB7;
stroke-width: 15px;
}
......@@ -24,111 +24,92 @@ Tests for charts and widgets.
from weblate.trans.tests.test_views import ViewTestCase
from django.core.urlresolvers import reverse
import json
class ChartsTest(ViewTestCase):
'''
Testing of charts.
'''
def test_activity_html(self):
'''
Test of html for activity charts.
'''
response = self.client.get(
reverse('view_activity')
)
self.assertContains(response, 'img src="/activity')
response = self.client.get(
reverse('view_activity_project', kwargs=self.kw_project)
)
self.assertContains(response, 'img src="/activity')
response = self.client.get(
reverse('view_activity_subproject', kwargs=self.kw_subproject)
)
self.assertContains(response, 'img src="/activity')
response = self.client.get(
reverse('view_activity_translation', kwargs=self.kw_translation)
)
self.assertContains(response, 'img src="/activity')
response = self.client.get(
reverse('view_language_activity', kwargs={'lang': 'cs'})
)
self.assertContains(response, 'img src="/activity')
def assert_json_chart_data(self, response):
"""
Tests whether response has valid json chart data.
"""
self.assertEquals(response.get('Content-Type'), 'application/json')
data = json.loads(response.content)
self.assertTrue('series' in data)
self.assertTrue('labels' in data)
def test_activity_monthly(self):
'''
Test of monthly activity charts.
'''
response = self.client.get(
reverse('monthly_activity')
reverse('json_monthly_activity')
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('monthly_activity_project', kwargs=self.kw_project)
reverse('json_monthly_activity', kwargs=self.kw_project)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('monthly_activity_subproject', kwargs=self.kw_subproject)
reverse('json_monthly_activity', kwargs=self.kw_subproject)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('monthly_activity_translation', kwargs=self.kw_translation)
reverse('json_monthly_activity', kwargs=self.kw_translation)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('monthly_language_activity', kwargs={'lang': 'cs'})
reverse('json_monthly_activity', kwargs={'lang': 'cs'})
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse(
'monthly_user_activity',
'json_monthly_activity',
kwargs={'user': self.user.username}
)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
def test_activity_yearly(self):
'''
Test of yearly activity charts.
'''
response = self.client.get(
reverse('yearly_activity')
reverse('json_yearly_activity')
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('yearly_activity_project', kwargs=self.kw_project)
reverse('json_yearly_activity', kwargs=self.kw_project)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('yearly_activity_subproject', kwargs=self.kw_subproject)
reverse('json_yearly_activity', kwargs=self.kw_subproject)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('yearly_activity_translation', kwargs=self.kw_translation)
reverse('json_yearly_activity', kwargs=self.kw_translation)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse('yearly_language_activity', kwargs={'lang': 'cs'})
reverse('json_yearly_activity', kwargs={'lang': 'cs'})
)
self.assertPNG(response)
self.assert_json_chart_data(response)
response = self.client.get(
reverse(
'yearly_user_activity',
'json_yearly_activity',
kwargs={'user': self.user.username}
)
)
self.assertPNG(response)
self.assert_json_chart_data(response)
......@@ -24,288 +24,125 @@ Charting library for Weblate.
from weblate.trans.models import Change
from weblate.lang.models import Language
from weblate.trans.views.helper import get_project_translation
from django.shortcuts import render, get_object_or_404
from django.shortcuts import get_object_or_404
from django.http import HttpResponse
from django.contrib.auth.models import User
from cStringIO import StringIO
from django.core.urlresolvers import reverse
from PIL import Image, ImageDraw
from weblate.trans.fonts import get_font
def render_activity(activity):
'''
Helper for rendering activity charts.
'''
# Preprocess data for chart
maximum = max([l[1] for l in activity] + [1])
step = 780.0 / len(activity)
# Prepare image
image = Image.new('RGB', (800, 100), 'white')
draw = ImageDraw.Draw(image)
# Render axises
draw.line(((15, 5), (15, 85), (795, 85)), fill='black')
# Load font
font = get_font(11)
# Create Y axis label
y_label = str(maximum)
text = Image.new('L', font.getsize(y_label), 'white')
draw_txt = ImageDraw.Draw(text)
draw_txt.text((0, 0), y_label, font=font, fill='black')
text = text.transpose(Image.ROTATE_90)
image.paste(text, (2, 5))
# Counter for rendering ticks
last = -40
# Render activity itself
for offset, value in enumerate(activity):
# Calculate position
current = offset * step
# Render bar
draw.rectangle(
(
20 + current,
84,
20 + current + (step / 2),
84 - value[1] * 78.0 / maximum
),
fill=(0, 67, 118)
)
# Skip axis labels if they are too frequent
if current < last + 40:
continue
last = current
# X-Axis ticks
draw.text(
(15 + current, 86),
value[0].strftime('%m/%d'),
font=font, fill='black'
from django.utils.translation import pgettext
import json
def get_json_stats(request, days, step, project=None, subproject=None,
lang=None, user=None):
"""
Parse json stats URL params.
"""
if project is None and lang is None and user is None:
project = None
subproject = None
translation = None
language = None
user = None
elif user is not None:
project = None
subproject = None
translation = None
language = None
user = get_object_or_404(User, username=user)
elif project is None:
project = None
subproject = None
translation = None
language = get_object_or_404(Language, code=lang)
user = None
else:
# Process parameters
project, subproject, translation = get_project_translation(
request,
project,
subproject,
lang
)
# Render surface to PNG
out = StringIO()
image.convert('P', palette=Image.ADAPTIVE).save(out, 'PNG')
# Return response
return HttpResponse(content_type='image/png', content=out.getvalue())
def monthly_activity(request, project=None, subproject=None, lang=None):
'''
Show monthly activity chart.
'''
# Process parameters
project, subproject, translation = get_project_translation(
request,
project,
subproject,
lang
)
language = None
user = None
# Get actual stats
activity = Change.objects.month_stats(
project,
subproject,
translation
)
# Render chart
return render_activity(activity)
def yearly_activity(request, project=None, subproject=None, lang=None):
'''
Show yearly activity chart.
'''
# Process parameters
project, subproject, translation = get_project_translation(
request,
return Change.objects.base_stats(
days,
step,
project,
subproject,
lang
)
# Get actual stats
activity = Change.objects.year_stats(
project,
subproject,
translation
)
# Render chart
return render_activity(activity)
def monthly_language_activity(request, lang):
'''
Show monthly activity chart.
'''
# Process parameters
language = get_object_or_404(Language, code=lang)
# Get actual stats
activity = Change.objects.month_stats(
language=language
)
# Render chart
return render_activity(activity)
def yearly_language_activity(request, lang):
'''
Show yearly activity chart.
'''
# Process parameters
language = get_object_or_404(Language, code=lang)
# Get actual stats
activity = Change.objects.year_stats(
language=language
translation,
language,
user
)
# Render chart
return render_activity(activity)
def monthly_user_activity(request, user):
'''
Show monthly activity chart.
'''
# Process parameters
user = get_object_or_404(User, username=user)
# Get actual stats
activity = Change.objects.month_stats(
user=user
def json_yearly_activity(request, project=None, subproject=None, lang=None,
user=None):
"""
Returns yearly activity for matching changes as json.
"""
activity = get_json_stats(
request, 365, 7,
project, subproject, lang
)
# Render chart
return render_activity(activity)
def yearly_user_activity(request, user):
'''
Show yearly activity chart.
'''
# Process parameters
user = get_object_or_404(User, username=user)
# Get actual stats
activity = Change.objects.year_stats(
user=user
# Format
serie = []
labels = []
month = -1
for item in activity:
serie.append(item[1])
if month != item[0].month:
labels.append(
pgettext(
'Format string for yearly activity chart',
'{month}/{year}'
).format(
month=item[0].month,
year=item[0].year,
)
)
month = item[0].month
else:
labels.append('')
return HttpResponse(
content_type='application/json',
content=json.dumps({'series': [serie], 'labels': labels})
)
# Render chart
return render_activity(activity)
def view_activity(request, project=None, subproject=None, lang=None):
'''
Show html with activity charts.
'''
# Process parameters
project, subproject, translation = get_project_translation(
request,
project,
subproject,
lang
)
if translation is not None:
kwargs = {
'project': project.slug,
'subproject': subproject.slug,
'lang': translation.language.code,
}
monthly_url = reverse(
'monthly_activity_translation',
kwargs=kwargs
)
yearly_url = reverse(
'yearly_activity_translation',
kwargs=kwargs
)
elif subproject is not None:
kwargs = {
'project': project.slug,
'subproject': subproject.slug,
}
monthly_url = reverse(
'monthly_activity_subproject',
kwargs=kwargs
)
yearly_url = reverse(
'yearly_activity_subproject',
kwargs=kwargs
)
elif project is not None:
kwargs = {
'project': project.slug,
}
monthly_url = reverse(
'monthly_activity_project',
kwargs=kwargs
)
yearly_url = reverse(
'yearly_activity_project',
kwargs=kwargs
)
else:
monthly_url = reverse(
'monthly_activity',
)
yearly_url = reverse(
'yearly_activity',
)
return render(
request,
'js/activity.html',
{
'yearly_url': yearly_url,
'monthly_url': monthly_url,
}
)
def view_language_activity(request, lang):
'''
Show html with activity charts.
'''
# Process parameters
language = get_object_or_404(Language, code=lang)
monthly_url = reverse(
'monthly_language_activity',
kwargs={'lang': language.code},
)
yearly_url = reverse(
'yearly_language_activity',
kwargs={'lang': language.code},
def json_monthly_activity(request, project=None, subproject=None, lang=None,
user=None):
"""
Returns monthly activity for matching changes as json.
"""
activity = get_json_stats(
request, 31, 1,
project, subproject, lang
)
return render(
request,
'js/activity.html',
{
'yearly_url': yearly_url,
'monthly_url': monthly_url,
}
# Format
serie = []
labels = []
for pos, item in enumerate(activity):
serie.append(item[1])
if pos % 5 == 0:
labels.append(
pgettext(
'Format string for monthly activity chart',
'{day}/{month}'
).format(
day=item[0].day,
month=item[0].month,
year=item[0].year,
)
)
else:
labels.append('')
return HttpResponse(
content_type='application/json',
content=json.dumps({'series': [serie], 'labels': labels})
)
......@@ -202,99 +202,68 @@ urlpatterns = patterns(
name='delete-user',
),
# Activity HTML
url(
r'^activity/html/$',
'weblate.trans.views.charts.view_activity',
name='view_activity',
),
# Monthly activity
url(
r'^activity/html/' + PROJECT + '$',
'weblate.trans.views.charts.view_activity',
name='view_activity_project',
r'^activity-json/month/$',
'weblate.trans.views.charts.json_monthly_activity',
name='json_monthly_activity',
),
url(
r'^activity/html/' + SUBPROJECT + '$',
'weblate.trans.views.charts.view_activity',
name='view_activity_subproject',
r'^activity-json/month/' + PROJECT + '$',
'weblate.trans.views.charts.json_monthly_activity',
name='json_monthly_activity',
),
url(
r'^activity/html/' + TRANSLATION + '$',
'weblate.trans.views.charts.view_activity',
name='view_activity_translation',
r'^activity-json/month/' + SUBPROJECT + '$',
'weblate.trans.views.charts.json_monthly_activity',
name='json_monthly_activity',
),
# Monthly activity
url(
r'^activity/month/$',
'weblate.trans.views.charts.monthly_activity',
name='monthly_activity',
r'^activity-json/month/' + TRANSLATION + '$',
'weblate.trans.views.charts.json_monthly_activity',
name='json_monthly_activity',
),
url(
r'^activity/month/' + PROJECT + '$',
'weblate.trans.views.charts.monthly_activity',
name='monthly_activity_project',
r'^activity-json/language/month/' + LANGUAGE + '/$',
'weblate.trans.views.charts.json_monthly_activity',
name='json_monthly_activity',
),
url(
r'^activity/month/' + SUBPROJECT + '$',
'weblate.trans.views.charts.monthly_activity',
name='monthly_activity_subproject',
),
url(
r'^activity/month/' + TRANSLATION + '$',
'weblate.trans.views.charts.monthly_activity',
name='monthly_activity_translation',
r'^activity/user/month/(?P<user>[^/]+)/$',
'weblate.trans.views.charts.json_monthly_activity',
name='json_monthly_activity',
),
# Yearly activity
url(
r'^activity/year/$',
'weblate.trans.views.charts.yearly_activity',
name='yearly_activity',
r'^activity-json/year/$',
'weblate.trans.views.charts.json_yearly_activity',
name='json_yearly_activity',
),
url(
r'^activity/year/' + PROJECT + '$',
'weblate.trans.views.charts.yearly_activity',
name='yearly_activity_project',
r'^activity-json/year/' + PROJECT + '$',
'weblate.trans.views.charts.json_yearly_activity',
name='json_yearly_activity',
),
url(
r'^activity/year/' + SUBPROJECT + '$',
'weblate.trans.views.charts.yearly_activity',
name='yearly_activity_subproject',
r'^activity-json/year/' + SUBPROJECT + '$',
'weblate.trans.views.charts.json_yearly_activity',
name='json_yearly_activity',
),
url(
r'^activity/year/' + TRANSLATION + '$',
'weblate.trans.views.charts.yearly_activity',
name='yearly_activity_translation',
r'^activity-json/year/' + TRANSLATION + '$',
'weblate.trans.views.charts.json_yearly_activity',
name='json_yearly_activity',
),
# Per language activity
url(
r'^activity/language/html/' + LANGUAGE + '/$',
'weblate.trans.views.charts.view_language_activity',
name='view_language_activity',
),
url(
r'^activity/language/month/' + LANGUAGE + '/$',
'weblate.trans.views.charts.monthly_language_activity',
name='monthly_language_activity',
),
url(
r'^activity/language/year/' + LANGUAGE + '/$',
'weblate.trans.views.charts.yearly_language_activity',
name='yearly_language_activity',
),
# Per user activity
url(
r'^activity/user/month/(?P<user>[^/]+)/$',
'weblate.trans.views.charts.monthly_user_activity',
name='monthly_user_activity',
r'^activity-json/language/year/' + LANGUAGE + '/$',
'weblate.trans.views.charts.json_yearly_activity',
name='json_yearly_activity',
),
url(
r'^activity/user/year/(?P<user>[^/]+)/$',
'weblate.trans.views.charts.yearly_user_activity',
name='yearly_user_activity',
'weblate.trans.views.charts.json_yearly_activity',
name='json_yearly_activity',
),
# Comments
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment