diff --git a/TODO.md b/TODO.md index 9c17d763..5c4f68c5 100644 --- a/TODO.md +++ b/TODO.md @@ -365,7 +365,6 @@ method( arg, arg, arg) - Bash/Python/PHPBackend # services.handler as generator in order to save memory? not swell like a balloon @@ -385,8 +384,6 @@ uwsgi --reload /tmp/project-master.pid # or if uwsgi was started with touch-reload=/tmp/somefile touch /tmp/somefile -# batch zone edditing - # datetime metric storage granularity: otherwise innacurate detection of billed metric on order.billed_on # Serializers.validation migration to DRF3: grep -r 'attrs, source' *|grep -v '~' @@ -422,18 +419,10 @@ Case # case on payment transaction state ? case when trans.amount > -# Resource data inline show info: link to monitor data, and history chart: link to monitor data of each item - +# Resource inline links point to custom changelist view that preserve state (breadcrumbs, title, etc) rather than generic changeview with queryarg filtering # ORDER diff Pending vs ALL # pre-bill confirmation: remove account if lines.count() == 0 ? # Discount prepaid metric should be more optimal https://orchestra.pangea.org/admin/orders/order/40/ # -> order.billed_metric besides billed_until - -# USE CONTROLLED MONITOR GRAPHS -# graph metric storage -# CLEANUP MONITOR DATA 0.00 (from SUM aggregators, AVG are needed for maintaining state), maybe also aggregate older values -# MULTIPLE GRAPH TYPE: datapoint line chart for AVG, stacked for SUM()) - - diff --git a/orchestra/contrib/resources/actions.py b/orchestra/contrib/resources/actions.py index 97933254..4e1d3b71 100644 --- a/orchestra/contrib/resources/actions.py +++ b/orchestra/contrib/resources/actions.py @@ -42,9 +42,8 @@ def run_monitor(modeladmin, request, queryset): run_monitor.url_name = 'monitor' -def history(modeladmin, request, queryset): +def show_history(modeladmin, request, queryset): context = { 'ids': ','.join(map(str, queryset.values_list('id', flat=True))), } return render(request, 'admin/resources/resourcedata/history.html', context) -history.url_name = 'history' diff --git a/orchestra/contrib/resources/admin.py b/orchestra/contrib/resources/admin.py index bbcb7240..66062f31 100644 --- a/orchestra/contrib/resources/admin.py +++ b/orchestra/contrib/resources/admin.py @@ -1,12 +1,15 @@ from urllib.parse import parse_qs +from django.apps import apps from django.conf.urls import url from django.contrib import admin, messages from django.contrib.contenttypes.admin import GenericTabularInline from django.contrib.contenttypes.forms import BaseGenericInlineFormSet from django.contrib.admin.utils import unquote from django.core.urlresolvers import reverse +from django.db.models import Q from django.shortcuts import redirect +from django.templatetags.static import static from django.utils.functional import cached_property from django.utils.safestring import mark_safe from django.utils.translation import ungettext, ugettext, ugettext_lazy as _ @@ -18,7 +21,7 @@ from orchestra.core import services from orchestra.utils import db, sys from orchestra.utils.functional import cached -from .actions import run_monitor, history +from .actions import run_monitor, show_history from .api import history_data from .filters import ResourceDataListFilter from .forms import ResourceForm @@ -120,7 +123,7 @@ class ResourceDataAdmin(ExtendedModelAdmin): ) search_fields = ('content_object_repr',) readonly_fields = fields - actions = (run_monitor, history) + actions = (run_monitor, show_history) change_view_actions = actions ordering = ('-updated_at',) list_select_related = ('resource__content_type', 'content_type') @@ -166,15 +169,13 @@ class ResourceDataAdmin(ExtendedModelAdmin): return redirect(url) def list_related_view(self, request, app_name, model_name, object_id): - from django.apps import apps - from django.db.models import Q resources = Resource.objects.select_related('content_type') resource_models = {r.content_type.model_class(): r.content_type_id for r in resources} # Self model = apps.get_model(app_name, model_name) obj = model.objects.get(id=int(object_id)) ct_id = resource_models[model] - qset = Q(content_type_id=ct_id, object_id=obj.id) + qset = Q(content_type_id=ct_id, object_id=obj.id, resource__is_active=True) # Related for field, rel in obj._meta.fields_map.items(): try: @@ -184,7 +185,7 @@ class ResourceDataAdmin(ExtendedModelAdmin): else: manager = getattr(obj, field) ids = manager.values_list('id', flat=True) - qset = Q(qset) | Q(content_type_id=ct_id, object_id__in=ids) + qset = Q(qset) | Q(content_type_id=ct_id, object_id__in=ids, resource__is_active=True) related = ResourceData.objects.filter(qset) related_ids = related.values_list('id', flat=True) related_ids = ','.join(map(str, related_ids)) @@ -211,7 +212,7 @@ class MonitorDataAdmin(ExtendedModelAdmin): mdata = ResourceData.objects.get(pk=int(resource_data[0])) resource = mdata.resource ids = [] - for dataset in mdata.get_monitor_datasets(): + for monitor, dataset in mdata.get_monitor_datasets(): dataset = resource.aggregation_instance.filter(dataset) if isinstance(dataset, MonitorData): ids.append(dataset.id) @@ -302,9 +303,8 @@ def resource_inline_factory(resources): return super(ResourceInline, self).get_fieldsets(request, obj) def display_used(self, rdata): - from django.templatetags.static import static - update_link = '' - history_link = '' + update = '' + history = '' if rdata.pk: context = { 'title': _("Update"), @@ -315,13 +315,15 @@ def resource_inline_factory(resources): context.update({ 'title': _("Show history"), 'image': '' % static('orchestra/images/history.png'), - 'url': reverse('admin:resources_resourcedata_history', args=(rdata.pk,)), + 'url': reverse('admin:resources_resourcedata_show_history', args=(rdata.pk,)), 'popup': 'onclick="return showAddAnotherPopup(this);"', }) history = '%(image)s' % context if rdata.used is not None: - return ' '.join(map(str, (rdata.used, rdata.resource.unit, update, history))) - return _("Unknonw %s") % update_link + used_url = reverse('admin:resources_resourcedata_used_monitordata', args=(rdata.pk,)) + used = '%s %s' % (used_url, rdata.used, rdata.unit) + return ' '.join(map(str, (used, update, history))) + return _("Unknonw %s %s") % (update, history) display_used.short_description = _("Used") display_used.allow_tags = True diff --git a/orchestra/contrib/resources/api.py b/orchestra/contrib/resources/api.py index 0efb4815..9f89fb1e 100644 --- a/orchestra/contrib/resources/api.py +++ b/orchestra/contrib/resources/api.py @@ -11,9 +11,5 @@ def history_data(request): ids = map(int, parse_qs(request.META['QUERY_STRING'])['ids'][0].split(',')) queryset = ResourceData.objects.filter(id__in=ids) history = get_history_data(queryset) - def default(obj): - if isinstance(obj, set): - return list(obj) - return obj - response = json.dumps(history, default=default, indent=4) + response = json.dumps(history, indent=4) return HttpResponse(response, content_type="application/json") diff --git a/orchestra/contrib/resources/helpers.py b/orchestra/contrib/resources/helpers.py index 3611ad95..6b560fa8 100644 --- a/orchestra/contrib/resources/helpers.py +++ b/orchestra/contrib/resources/helpers.py @@ -1,4 +1,4 @@ -from django.template.defaultfilters import date as date_filter +from django.template.defaultfilters import date as date_format def get_history_data(queryset): @@ -18,7 +18,7 @@ def get_history_data(queryset): 'unit': resource.unit, 'scale': resource.get_scale(), 'verbose_name': str(resource.verbose_name), - 'dates': set(), + 'dates': set() if aggregation.aggregated_history else None, 'objects': [], } resources[resource] = (options, aggregation) @@ -33,10 +33,9 @@ def get_history_data(queryset): needs_aggregation = True serie = {} for data in datas: - date = date_filter(data.date) value = round(float(data.value)/scale, 3) if data.value is not None else None - all_dates.add(date) - serie[date] = value + all_dates.add(data.date) + serie[data.date] = value else: serie = [] for data in datas: @@ -62,7 +61,8 @@ def get_history_data(queryset): result = [] for options, aggregation in resources.values(): if aggregation.aggregated_history: - all_dates = options['dates'] + all_dates = sorted(options['dates']) + options['dates'] = [date_format(date) for date in all_dates] for obj in options['objects']: for monitor in obj['monitors']: series = [] diff --git a/orchestra/contrib/resources/templates/admin/resources/resourcedata/history.html b/orchestra/contrib/resources/templates/admin/resources/resourcedata/history.html index 0e7b98ce..11291d25 100644 --- a/orchestra/contrib/resources/templates/admin/resources/resourcedata/history.html +++ b/orchestra/contrib/resources/templates/admin/resources/resourcedata/history.html @@ -4,6 +4,7 @@