Add stat comparison

This commit is contained in:
R. Miles McCain 2020-04-20 21:15:32 -04:00
parent 1691eb4550
commit 2a0e5ea6bf
No known key found for this signature in database
GPG Key ID: 91CB47BDDF2671A5
5 changed files with 111 additions and 2 deletions

View File

@ -59,6 +59,13 @@ class Service(models.Model):
if end_time is None: if end_time is None:
end_time = timezone.now() end_time = timezone.now()
main_data = self.get_relative_stats(start_time, end_time)
comparison_data = self.get_relative_stats(start_time - (end_time - start_time), start_time)
main_data["compare"] = comparison_data
return main_data
def get_relative_stats(self, start_time, end_time):
Session = apps.get_model("analytics", "Session") Session = apps.get_model("analytics", "Session")
Hit = apps.get_model("analytics", "Hit") Hit = apps.get_model("analytics", "Hit")

View File

@ -12,17 +12,24 @@
<div class="grid grid-cols-2 md:grid-cols-4 gap-6"> <div class="grid grid-cols-2 md:grid-cols-4 gap-6">
<div> <div>
<p>Sessions</p> <p>Sessions</p>
<p class="label">{{stats.session_count|intcomma}}</p> <p class="label">
{{stats.session_count|intcomma}}
{% compare stats.compare.session_count stats.session_count "UP" %}
</p>
</div> </div>
<div> <div>
<p>Hits</p> <p>Hits</p>
<p class="label">{{stats.hit_count|intcomma}}</p> <p class="label">
{{stats.hit_count|intcomma}}
{% compare stats.compare.hit_count stats.hit_count "UP" %}
</p>
</div> </div>
<div> <div>
<p>Bounce Rate</p> <p>Bounce Rate</p>
<p class="label"> <p class="label">
{% if stats.bounce_rate_pct != None %} {% if stats.bounce_rate_pct != None %}
{{stats.bounce_rate_pct|floatformat:"-1"}}% {{stats.bounce_rate_pct|floatformat:"-1"}}%
{% compare stats.compare.bounce_rate_pct stats.bounce_rate_pct "DOWN" %}
{% else %} {% else %}
? ?
{% endif %} {% endif %}
@ -33,6 +40,7 @@
<p class="label"> <p class="label">
{% if stats.avg_session_duration != None %} {% if stats.avg_session_duration != None %}
{{stats.avg_session_duration|naturaldelta}} {{stats.avg_session_duration|naturaldelta}}
{% compare stats.compare.avg_session_duration stats.avg_session_duration "UP" %}
{% else %} {% else %}
? ?
{% endif %} {% endif %}

View File

@ -0,0 +1,7 @@
{% load helpers %}
<span
class="{{classes}} {% relative_stat_tone start end good good_classes=good_classes bad_classes=bad_classes neutral_classes=neutral_classes %}"
title="Compared against equal period prior">
{% percent_change_display start end %}
</span>

View File

@ -12,16 +12,23 @@
{% block service_content %} {% block service_content %}
<div class="grid grid-cols-2 gap-6 md:flex justify-between mb-6 card ~neutral !high px-6" id="stats"> <div class="grid grid-cols-2 gap-6 md:flex justify-between mb-6 card ~neutral !high px-6" id="stats">
{% with classes="text-sm font-semibold" good_classes="text-green-400" bad_classes="text-red-400" neutral_classes="text-gray-400" %}
<article class=""> <article class="">
<p class="label text-gray-400">Sessions</p> <p class="label text-gray-400">Sessions</p>
<p class="heading"> <p class="heading">
{{stats.session_count|intcomma}} {{stats.session_count|intcomma}}
<div>
{% compare stats.compare.session_count stats.session_count "UP" classes=classes good_classes=good_classes bad_classes=bad_classes neutral_classes=neutral_classes %}
</div>
</p> </p>
</article> </article>
<article class=""> <article class="">
<p class="label text-gray-400">Hits</p> <p class="label text-gray-400">Hits</p>
<p class="heading"> <p class="heading">
{{stats.hit_count|intcomma}} {{stats.hit_count|intcomma}}
<div>
{% compare stats.compare.hit_count stats.hit_count "UP" classes=classes good_classes=good_classes bad_classes=bad_classes neutral_classes=neutral_classes %}
</div>
</p> </p>
</article> </article>
<article class=""> <article class="">
@ -29,6 +36,9 @@
<p class="heading"> <p class="heading">
{% if stats.avg_load_time %} {% if stats.avg_load_time %}
{{stats.avg_load_time|floatformat:"0"}}ms {{stats.avg_load_time|floatformat:"0"}}ms
<div>
{% compare stats.compare.avg_load_time stats.avg_load_time "DOWN" classes=classes good_classes=good_classes bad_classes=bad_classes neutral_classes=neutral_classes %}
</div>
{% else %} {% else %}
? ?
{% endif %} {% endif %}
@ -39,6 +49,9 @@
<p class="heading"> <p class="heading">
{% if stats.bounce_rate_pct %} {% if stats.bounce_rate_pct %}
{{stats.bounce_rate_pct|floatformat:"-1"}}% {{stats.bounce_rate_pct|floatformat:"-1"}}%
<div>
{% compare stats.compare.bounce_rate_pct stats.bounce_rate_pct "DOWN" classes=classes good_classes=good_classes bad_classes=bad_classes neutral_classes=neutral_classes %}
</div>
{% else %} {% else %}
? ?
{% endif %} {% endif %}
@ -49,6 +62,9 @@
<p class="heading"> <p class="heading">
{% if stats.avg_session_duration %} {% if stats.avg_session_duration %}
{{stats.avg_session_duration|naturaldelta}} {{stats.avg_session_duration|naturaldelta}}
<div>
{% compare stats.compare.avg_session_duration stats.avg_session_duration "UP" classes=classes good_classes=good_classes bad_classes=bad_classes neutral_classes=neutral_classes %}
</div>
{% else %} {% else %}
? ?
{% endif %} {% endif %}
@ -59,11 +75,15 @@
<p class="heading"> <p class="heading">
{% if stats.avg_hits_per_session %} {% if stats.avg_hits_per_session %}
{{stats.avg_hits_per_session|floatformat:"-1"}} {{stats.avg_hits_per_session|floatformat:"-1"}}
<div>
{% compare stats.compare.avg_hits_per_session stats.avg_hits_per_session "UP" classes=classes good_classes=good_classes bad_classes=bad_classes neutral_classes=neutral_classes %}
</div>
{% else %} {% else %}
? ?
{% endif %} {% endif %}
</p> </p>
</article> </article>
{% endwith %}
</div> </div>
<div class="card ~neutral !low py-0 mb-6"> <div class="card ~neutral !low py-0 mb-6">
{% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data %} {% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data %}

View File

@ -30,6 +30,7 @@ def flag_emoji(isocode):
except: except:
return "" return ""
@register.filter @register.filter
def country_name(isocode): def country_name(isocode):
try: try:
@ -37,6 +38,72 @@ def country_name(isocode):
except: except:
return "Unknown" return "Unknown"
@register.simple_tag
def relative_stat_tone(
start,
end,
good="UP",
good_classes=None,
bad_classes=None,
neutral_classes=None,
):
good_classes = good_classes or "~positive"
bad_classes = bad_classes or "~critical"
neutral_classes = neutral_classes or "~neutral"
if start == None or end == None or start == end:
return neutral_classes
if good == "UP":
return good_classes if start <= end else bad_classes
elif good == "DOWN":
return bad_classes if start <= end else good_classes
else:
return neutral_classes
@register.simple_tag
def percent_change_display(start, end):
if start == None or end == None:
return SafeString("&Delta; n/a")
if start == end:
direction = ""
else:
direction = "&uarr; " if end > start else "&darr; "
if start == 0 and end != 0:
pct_change = "100%"
else:
change = int(round(100 * abs(end - start) / start))
if change > 999:
return "> 999%"
else:
pct_change = str(change) + "%"
return SafeString(direction + pct_change)
@register.inclusion_tag("dashboard/includes/stat_comparison.html")
def compare(
start,
end,
good,
classes="badge",
good_classes=None,
bad_classes=None,
neutral_classes=None,
):
return {
"start": start,
"end": end,
"good": good,
"classes": classes,
"good_classes": good_classes,
"bad_classes": bad_classes,
"neutral_classes": neutral_classes,
}
@register.filter @register.filter
def startswith(text, starts): def startswith(text, starts):
if isinstance(text, str): if isinstance(text, str):