Query a period of time for usage summary
Allows the user to pick a range of time to query services usage data. Fallback mode: the user will have to input dates by hand if JavaScript is disabled. Change-Id: I4cf4d4fd56fd450743b9d624dc066d6ed74df881 Fixes: bug #1102448
This commit is contained in:
parent
699926413c
commit
142a6e97fe
@ -20,8 +20,6 @@
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms.forms import NON_FIELD_ERRORS
|
from django.forms.forms import NON_FIELD_ERRORS
|
||||||
from django.utils import dates
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
|
|
||||||
class SelfHandlingMixin(object):
|
class SelfHandlingMixin(object):
|
||||||
@ -49,13 +47,11 @@ class SelfHandlingForm(SelfHandlingMixin, forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class DateForm(forms.Form):
|
class DateForm(forms.Form):
|
||||||
""" A simple form for selecting a start date. """
|
""" A simple form for selecting a range of time. """
|
||||||
month = forms.ChoiceField(choices=dates.MONTHS.items())
|
start = forms.DateField(input_formats=("%Y-%m-%d",))
|
||||||
year = forms.ChoiceField()
|
end = forms.DateField(input_formats=("%Y-%m-%d",))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DateForm, self).__init__(*args, **kwargs)
|
super(DateForm, self).__init__(*args, **kwargs)
|
||||||
years = [(year, year) for year
|
self.fields['start'].widget.attrs['data-date-format'] = "yyyy-mm-dd"
|
||||||
in xrange(2009, timezone.now().year + 1)]
|
self.fields['end'].widget.attrs['data-date-format'] = "yyyy-mm-dd"
|
||||||
years.reverse()
|
|
||||||
self.fields['year'].choices = years
|
|
||||||
|
@ -10,6 +10,7 @@ horizon.forms = {
|
|||||||
$volSize.val($option.data("size"));
|
$volSize.val($option.data("size"));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handle_image_source: function() {
|
handle_image_source: function() {
|
||||||
$("div.table_wrapper, #modal_wrapper").on("change", "select#id_image_source", function(evt) {
|
$("div.table_wrapper, #modal_wrapper").on("change", "select#id_image_source", function(evt) {
|
||||||
var $option = $(this).find("option:selected");
|
var $option = $(this).find("option:selected");
|
||||||
@ -19,6 +20,27 @@ horizon.forms = {
|
|||||||
var $volSize = $form.find('input#id_size');
|
var $volSize = $form.find('input#id_size');
|
||||||
$volSize.val($option.data("size"));
|
$volSize.val($option.data("size"));
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
datepicker: function() {
|
||||||
|
var startDate = $('input#id_start').datepicker()
|
||||||
|
.on('changeDate', function(ev) {
|
||||||
|
if (ev.date.valueOf() > endDate.date.valueOf()) {
|
||||||
|
var newDate = new Date(ev.date)
|
||||||
|
newDate.setDate(newDate.getDate() + 1);
|
||||||
|
endDate.setValue(newDate);
|
||||||
|
$('input#id_end')[0].focus();
|
||||||
|
}
|
||||||
|
startDate.hide();
|
||||||
|
}).data('datepicker');
|
||||||
|
|
||||||
|
var endDate = $('input#id_end').datepicker({
|
||||||
|
onRender: function(date) {
|
||||||
|
return date.valueOf() < startDate.date.valueOf() ? 'disabled' : '';
|
||||||
|
}
|
||||||
|
}).on('changeDate', function(ev) {
|
||||||
|
endDate.hide();
|
||||||
|
}).data('datepicker');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,6 +100,7 @@ horizon.addInitFunction(function () {
|
|||||||
|
|
||||||
horizon.forms.handle_snapshot_source();
|
horizon.forms.handle_snapshot_source();
|
||||||
horizon.forms.handle_image_source();
|
horizon.forms.handle_image_source();
|
||||||
|
horizon.forms.datepicker();
|
||||||
|
|
||||||
// Bind event handlers to confirm dangerous actions.
|
// Bind event handlers to confirm dangerous actions.
|
||||||
$("body").on("click", "form button.btn-danger", function (evt) {
|
$("body").on("click", "form button.btn-danger", function (evt) {
|
||||||
|
@ -2,18 +2,19 @@
|
|||||||
|
|
||||||
<div class="usage_info_wrapper">
|
<div class="usage_info_wrapper">
|
||||||
<form action="" method="get" id="date_form" class="form-horizontal">
|
<form action="" method="get" id="date_form" class="form-horizontal">
|
||||||
<h3>{% trans "Select a month to query its usage" %}: </h3>
|
<h3>{% trans "Select a period of time to query its usage" %}: </h3>
|
||||||
<div class="form-row">
|
<div class="datepicker">
|
||||||
{{ form.month }}
|
<span>{% trans "From" %}: {{ form.start }} </span>
|
||||||
{{ form.year }}
|
<span>{% trans "To" %}: {{ form.end }} </span>
|
||||||
<button class="btn btn-small" type="submit">{% trans "Submit" %}</button>
|
<button class="btn btn-small" type="submit">{% trans "Submit" %}</button>
|
||||||
|
<small>{% trans "The date should be in YYYY-mm-dd format." %}</small>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p id="activity">
|
<p id="activity">
|
||||||
<span><strong>{% trans "Active Instances" %}:</strong> {{ usage.summary.instances|default:'-' }}</span>
|
<span><strong>{% trans "Active Instances" %}:</strong> {{ usage.summary.instances|default:'-' }}</span>
|
||||||
<span><strong>{% trans "Active RAM" %}:</strong> {{ usage.summary.memory_mb|mbformat|default:'-' }}</span>
|
<span><strong>{% trans "Active RAM" %}:</strong> {{ usage.summary.memory_mb|mbformat|default:'-' }}</span>
|
||||||
<span><strong>{% trans "This Month's VCPU-Hours" %}:</strong> {{ usage.summary.vcpu_hours|floatformat:2|default:'-' }}</span>
|
<span><strong>{% trans "This Period's VCPU-Hours" %}:</strong> {{ usage.summary.vcpu_hours|floatformat:2|default:'-' }}</span>
|
||||||
<span><strong>{% trans "This Month's GB-Hours" %}:</strong> {{ usage.summary.disk_gb_hours|floatformat:2|default:'-' }}</span>
|
<span><strong>{% trans "This Period's GB-Hours" %}:</strong> {{ usage.summary.disk_gb_hours|floatformat:2|default:'-' }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,6 @@ from django.core.urlresolvers import reverse
|
|||||||
from django import http
|
from django import http
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from mox import Func
|
|
||||||
from mox import IsA
|
from mox import IsA
|
||||||
|
|
||||||
from horizon.templatetags.sizeformat import mbformat
|
from horizon.templatetags.sizeformat import mbformat
|
||||||
@ -47,10 +46,14 @@ class UsageViewTests(test.BaseAdminViewTests):
|
|||||||
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn([self.tenants.list(), False])
|
.AndReturn([self.tenants.list(), False])
|
||||||
api.nova.usage_list(IsA(http.HttpRequest),
|
api.nova.usage_list(IsA(http.HttpRequest),
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year,
|
||||||
Func(usage.almost_now)) \
|
now.month,
|
||||||
.AndReturn([usage_obj])
|
now.day, 0, 0, 0, 0),
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
datetime.datetime(now.year,
|
||||||
|
now.month,
|
||||||
|
now.day, 23, 59, 59, 0)) \
|
||||||
|
.AndReturn([usage_obj])
|
||||||
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.limits['absolute'])
|
.AndReturn(self.limits['absolute'])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
res = self.client.get(reverse('horizon:admin:overview:index'))
|
res = self.client.get(reverse('horizon:admin:overview:index'))
|
||||||
@ -78,9 +81,13 @@ class UsageViewTests(test.BaseAdminViewTests):
|
|||||||
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn([self.tenants.list(), False])
|
.AndReturn([self.tenants.list(), False])
|
||||||
api.nova.usage_list(IsA(http.HttpRequest),
|
api.nova.usage_list(IsA(http.HttpRequest),
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year,
|
||||||
Func(usage.almost_now)) \
|
now.month,
|
||||||
.AndReturn(usage_obj)
|
now.day, 0, 0, 0, 0),
|
||||||
|
datetime.datetime(now.year,
|
||||||
|
now.month,
|
||||||
|
now.day, 23, 59, 59, 0)) \
|
||||||
|
.AndReturn(usage_obj)
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndReturn(self.limits['absolute'])
|
.AndReturn(self.limits['absolute'])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
@ -24,7 +24,6 @@ from django.core.urlresolvers import reverse
|
|||||||
from django import http
|
from django import http
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from mox import Func
|
|
||||||
from mox import IsA
|
from mox import IsA
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
@ -42,8 +41,12 @@ class UsageViewTests(test.TestCase):
|
|||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||||
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
|
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year,
|
||||||
Func(usage.almost_now)) \
|
now.month,
|
||||||
|
now.day, 0, 0, 0, 0),
|
||||||
|
datetime.datetime(now.year,
|
||||||
|
now.month,
|
||||||
|
now.day, 23, 59, 59, 0)) \
|
||||||
.AndReturn(usage_obj)
|
.AndReturn(usage_obj)
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndReturn(self.limits['absolute'])
|
.AndReturn(self.limits['absolute'])
|
||||||
@ -60,8 +63,12 @@ class UsageViewTests(test.TestCase):
|
|||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||||
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
|
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year,
|
||||||
Func(usage.almost_now)) \
|
now.month,
|
||||||
|
now.day, 0, 0, 0, 0),
|
||||||
|
datetime.datetime(now.year,
|
||||||
|
now.month,
|
||||||
|
now.day, 23, 59, 59, 0)) \
|
||||||
.AndRaise(exc)
|
.AndRaise(exc)
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndReturn(self.limits['absolute'])
|
.AndReturn(self.limits['absolute'])
|
||||||
@ -78,14 +85,13 @@ class UsageViewTests(test.TestCase):
|
|||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||||
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0)
|
||||||
|
end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
start, end).AndReturn(usage_obj)
|
||||||
Func(usage.almost_now)) \
|
|
||||||
.AndReturn(usage_obj)
|
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndReturn(self.limits['absolute'])
|
.AndReturn(self.limits['absolute'])
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
res = self.client.get(reverse('horizon:project:overview:index') +
|
res = self.client.get(reverse('horizon:project:overview:index') +
|
||||||
@ -97,14 +103,14 @@ class UsageViewTests(test.TestCase):
|
|||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||||
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0)
|
||||||
|
end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
start, end).AndRaise(self.exceptions.nova)
|
||||||
Func(usage.almost_now)) \
|
|
||||||
.AndRaise(self.exceptions.nova)
|
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndReturn(self.limits['absolute'])
|
.AndReturn(self.limits['absolute'])
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||||
@ -116,12 +122,11 @@ class UsageViewTests(test.TestCase):
|
|||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||||
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0)
|
||||||
|
end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
start, end).AndReturn(usage_obj)
|
||||||
Func(usage.almost_now)) \
|
|
||||||
.AndReturn(usage_obj)
|
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndRaise(self.exceptions.nova)
|
.AndRaise(self.exceptions.nova)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
@ -135,14 +140,13 @@ class UsageViewTests(test.TestCase):
|
|||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||||
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0)
|
||||||
|
end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
start, end).AndReturn(usage_obj)
|
||||||
Func(usage.almost_now)) \
|
|
||||||
.AndReturn(usage_obj)
|
|
||||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndReturn(self.limits['absolute'])
|
.AndReturn(self.limits['absolute'])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.datepicker {
|
.datepicker {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -179,4 +180,4 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
@ -724,6 +724,19 @@ form label {
|
|||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.datepicker {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datepicker input {
|
||||||
|
width: 65px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datepicker .btn {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
form.horizontal .form-field {
|
form.horizontal .form-field {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
@ -2102,4 +2115,4 @@ div.network {
|
|||||||
#info_box h3 {font-size:9pt;line-height:20px;}
|
#info_box h3 {font-size:9pt;line-height:20px;}
|
||||||
#info_box p {margin:0;font-size:9pt;line-height:14px;}
|
#info_box p {margin:0;font-size:9pt;line-height:14px;}
|
||||||
#info_box a {margin:0;font-size:9pt;line-height:14px;}
|
#info_box a {margin:0;font-size:9pt;line-height:14px;}
|
||||||
#info_box .error {color:darkred;}
|
#info_box .error {color:darkred;}
|
@ -14,7 +14,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from openstack_dashboard.usage.base import almost_now
|
|
||||||
from openstack_dashboard.usage.base import BaseUsage
|
from openstack_dashboard.usage.base import BaseUsage
|
||||||
from openstack_dashboard.usage.base import GlobalUsage
|
from openstack_dashboard.usage.base import GlobalUsage
|
||||||
from openstack_dashboard.usage.base import ProjectUsage
|
from openstack_dashboard.usage.base import ProjectUsage
|
||||||
@ -26,7 +25,6 @@ from openstack_dashboard.usage.views import UsageView
|
|||||||
assert BaseUsage
|
assert BaseUsage
|
||||||
assert ProjectUsage
|
assert ProjectUsage
|
||||||
assert GlobalUsage
|
assert GlobalUsage
|
||||||
assert almost_now
|
|
||||||
assert UsageView
|
assert UsageView
|
||||||
assert BaseUsageTable
|
assert BaseUsageTable
|
||||||
assert ProjectUsageTable
|
assert ProjectUsageTable
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
from calendar import monthrange
|
|
||||||
from csv import DictWriter
|
from csv import DictWriter
|
||||||
from csv import writer
|
from csv import writer
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
@ -24,12 +24,6 @@ from openstack_dashboard.usage import quotas
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def almost_now(input_time):
|
|
||||||
now = timezone.make_naive(timezone.now(), timezone.utc)
|
|
||||||
# If we're less than a minute apart we'll assume success here.
|
|
||||||
return now - input_time < datetime.timedelta(seconds=30)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseUsage(object):
|
class BaseUsage(object):
|
||||||
show_terminated = False
|
show_terminated = False
|
||||||
|
|
||||||
@ -46,21 +40,14 @@ class BaseUsage(object):
|
|||||||
return timezone.now()
|
return timezone.now()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_start(year, month, day=1):
|
def get_start(year, month, day):
|
||||||
start = datetime.datetime(year, month, day, 0, 0, 0)
|
start = datetime.datetime(year, month, day, 0, 0, 0)
|
||||||
return timezone.make_aware(start, timezone.utc)
|
return timezone.make_aware(start, timezone.utc)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_end(year, month, day=1):
|
def get_end(year, month, day):
|
||||||
days_in_month = monthrange(year, month)[1]
|
end = datetime.datetime(year, month, day, 23, 59, 59)
|
||||||
period = datetime.timedelta(days=days_in_month)
|
return timezone.make_aware(end, timezone.utc)
|
||||||
end = BaseUsage.get_start(year, month, day) + period
|
|
||||||
# End our calculation at midnight of the given day.
|
|
||||||
date_end = datetime.datetime.combine(end, datetime.time(0, 0, 0))
|
|
||||||
date_end = timezone.make_aware(date_end, timezone.utc)
|
|
||||||
if date_end > timezone.now():
|
|
||||||
date_end = timezone.now()
|
|
||||||
return date_end
|
|
||||||
|
|
||||||
def get_instances(self):
|
def get_instances(self):
|
||||||
instance_list = []
|
instance_list = []
|
||||||
@ -69,24 +56,50 @@ class BaseUsage(object):
|
|||||||
|
|
||||||
def get_date_range(self):
|
def get_date_range(self):
|
||||||
if not hasattr(self, "start") or not hasattr(self, "end"):
|
if not hasattr(self, "start") or not hasattr(self, "end"):
|
||||||
args = (self.today.year, self.today.month)
|
args_start = args_end = (self.today.year,
|
||||||
|
self.today.month,
|
||||||
|
self.today.day)
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
args = (int(form.cleaned_data['year']),
|
start = form.cleaned_data['start']
|
||||||
int(form.cleaned_data['month']))
|
end = form.cleaned_data['end']
|
||||||
self.start = self.get_start(*args)
|
args_start = (start.year,
|
||||||
self.end = self.get_end(*args)
|
start.month,
|
||||||
|
start.day)
|
||||||
|
args_end = (end.year,
|
||||||
|
end.month,
|
||||||
|
end.day)
|
||||||
|
elif form.is_bound:
|
||||||
|
messages.error(self.request,
|
||||||
|
_("Invalid date format: "
|
||||||
|
"Using today as default."))
|
||||||
|
self.start = self.get_start(*args_start)
|
||||||
|
self.end = self.get_end(*args_end)
|
||||||
|
return self.start, self.end
|
||||||
|
|
||||||
|
def init_form(self):
|
||||||
|
today = datetime.date.today()
|
||||||
|
first = datetime.date(day=1, month=today.month, year=today.year)
|
||||||
|
if today.day in range(5):
|
||||||
|
self.end = first - datetime.timedelta(days=1)
|
||||||
|
self.start = datetime.date(day=1,
|
||||||
|
month=self.end.month,
|
||||||
|
year=self.end.year)
|
||||||
|
else:
|
||||||
|
self.end = today
|
||||||
|
self.start = first
|
||||||
return self.start, self.end
|
return self.start, self.end
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
if not hasattr(self, 'form'):
|
if not hasattr(self, 'form'):
|
||||||
if any(key in ['month', 'year'] for key in self.request.GET):
|
if any(key in ['start', 'end'] for key in self.request.GET):
|
||||||
# bound form
|
# bound form
|
||||||
self.form = forms.DateForm(self.request.GET)
|
self.form = forms.DateForm(self.request.GET)
|
||||||
else:
|
else:
|
||||||
# non-bound form
|
# non-bound form
|
||||||
self.form = forms.DateForm(initial={'month': self.today.month,
|
init = self.init_form()
|
||||||
'year': self.today.year})
|
self.form = forms.DateForm(initial={'start': init[0],
|
||||||
|
'end': init[1]})
|
||||||
return self.form
|
return self.form
|
||||||
|
|
||||||
def get_limits(self):
|
def get_limits(self):
|
||||||
@ -100,7 +113,7 @@ class BaseUsage(object):
|
|||||||
raise NotImplementedError("You must define a get_usage method.")
|
raise NotImplementedError("You must define a get_usage method.")
|
||||||
|
|
||||||
def summarize(self, start, end):
|
def summarize(self, start, end):
|
||||||
if start <= end <= self.today:
|
if start <= end and start <= self.today:
|
||||||
# The API can't handle timezone aware datetime, so convert back
|
# The API can't handle timezone aware datetime, so convert back
|
||||||
# to naive UTC just for this last step.
|
# to naive UTC just for this last step.
|
||||||
start = timezone.make_naive(start, timezone.utc)
|
start = timezone.make_naive(start, timezone.utc)
|
||||||
@ -110,10 +123,14 @@ class BaseUsage(object):
|
|||||||
except:
|
except:
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to retrieve usage information.'))
|
_('Unable to retrieve usage information.'))
|
||||||
else:
|
elif end < start:
|
||||||
messages.info(self.request,
|
messages.error(self.request,
|
||||||
_("You are viewing data for the future, "
|
_("Invalid time period. The end date should be "
|
||||||
"which may or may not exist."))
|
"more recent than the start date."))
|
||||||
|
elif start > self.today:
|
||||||
|
messages.error(self.request,
|
||||||
|
_("Invalid time period. You are requesting "
|
||||||
|
"data from the future which may not exist."))
|
||||||
|
|
||||||
for project_usage in self.usage_list:
|
for project_usage in self.usage_list:
|
||||||
project_summary = project_usage.get_summary()
|
project_summary = project_usage.get_summary()
|
||||||
@ -130,11 +147,13 @@ class BaseUsage(object):
|
|||||||
|
|
||||||
def csv_link(self):
|
def csv_link(self):
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
|
data = {}
|
||||||
if hasattr(form, "cleaned_data"):
|
if hasattr(form, "cleaned_data"):
|
||||||
data = form.cleaned_data
|
data = form.cleaned_data
|
||||||
else:
|
if not ('start' in data and 'end' in data):
|
||||||
data = {"month": self.today.month, "year": self.today.year}
|
data = {"start": self.today.date(), "end": self.today.date()}
|
||||||
return "?month=%s&year=%s&format=csv" % (data['month'], data['year'])
|
return "?start=%s&end=%s&format=csv" % (data['start'],
|
||||||
|
data['end'])
|
||||||
|
|
||||||
|
|
||||||
class GlobalUsage(BaseUsage):
|
class GlobalUsage(BaseUsage):
|
||||||
|
Loading…
Reference in New Issue
Block a user