diff --git a/dashboard/models.py b/dashboard/models.py index a24036951..b8832073a 100644 --- a/dashboard/models.py +++ b/dashboard/models.py @@ -1,6 +1,8 @@ import ast import calendar import datetime +import operator +from functools import reduce import requests from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation @@ -105,7 +107,6 @@ def _gather_data_periodic(self, since, period): scale but works for now. """ OFFSET = "2 hours" # HACK! - ctid = ContentType.objects.get_for_model(self).id c = connections["default"].cursor() c.execute( @@ -117,10 +118,14 @@ def _gather_data_periodic(self, since, period): AND object_id = %s AND timestamp >= %s GROUP BY 1;""", - [period, OFFSET, ctid, self.id, since], + [period, OFFSET, self.content_type.id, self.id, since], ) return [(calendar.timegm(t.timetuple()), float(m)) for (t, m) in c.fetchall()] + @property + def content_type(self): + return ContentType.objects.get_for_model(self) + class TracTicketMetric(Metric): query = models.TextField() @@ -249,6 +254,17 @@ def link(self): return self.urljoin(self.jenkins_root_url, "job", self.build_name) +class DatumQuerySet(models.QuerySet): + def metrics(self, *metrics): + """ + Return only the data from the given metrics. + """ + if not metrics: + return self.none() + qobjs = [models.Q(content_type=m.content_type, object_id=m.pk) for m in metrics] + return self.filter(reduce(operator.or_, qobjs)) + + class Datum(models.Model): metric = GenericForeignKey() content_type = models.ForeignKey( @@ -258,6 +274,8 @@ class Datum(models.Model): timestamp = models.DateTimeField(default=datetime.datetime.now) measurement = models.BigIntegerField() + objects = DatumQuerySet.as_manager() + class Meta: ordering = ["-timestamp"] get_latest_by = "timestamp" diff --git a/dashboard/views.py b/dashboard/views.py index fbe734809..a2a449119 100644 --- a/dashboard/views.py +++ b/dashboard/views.py @@ -1,6 +1,6 @@ import datetime +import operator -from django.contrib.contenttypes.models import ContentType from django.core.cache import cache from django.forms.models import model_to_dict from django.http.response import Http404, JsonResponse @@ -22,25 +22,21 @@ def index(request): metrics.extend( MC.objects.filter(show_on_dashboard=True).select_related("category") ) - - content_types = ContentType.objects.get_for_models(*metrics) - datum_queryset = Datum.objects.none() - for metric, content_type in content_types.items(): - datum_queryset = datum_queryset.union( - Datum.objects.filter( - content_type_id=content_type.id, object_id=metric.id - ).order_by("-timestamp")[0:1] - ) - - latest_datums = { - (datum.object_id, datum.content_type_id): datum for datum in datum_queryset + metrics = sorted(metrics, key=operator.attrgetter("display_position")) + + metric_latest_querysets = [ + Datum.objects.metrics(metric).order_by("-timestamp")[0:1] + for metric in metrics + ] + data_latest = Datum.objects.none().union(*metric_latest_querysets) + latest_by_metric = { + (datum.content_type_id, datum.object_id): datum for datum in data_latest } data = [] - for metric, content_type in content_types.items(): - if latest := latest_datums.get((metric.id, content_type.id)): - data.append({"metric": metric, "latest": latest}) - data = sorted(data, key=lambda elem: elem["metric"].display_position) + for metric in metrics: + latest = latest_by_metric.get((metric.content_type.pk, metric.pk)) + data.append({"metric": metric, "latest": latest}) cache.set(key, data, 60 * 60, version=generation) return render(request, "dashboard/index.html", {"data": data})