Add hits to chart data (#141)
* Add hits to chart data * Hide legend Co-authored-by: R. Miles McCain <github@sendmiles.email>
This commit is contained in:
parent
ab44ba8318
commit
83b20643d2
@ -198,6 +198,36 @@ class Service(models.Model):
|
|||||||
|
|
||||||
avg_hits_per_session = hit_count / session_count if session_count > 0 else None
|
avg_hits_per_session = hit_count / session_count if session_count > 0 else None
|
||||||
|
|
||||||
|
avg_session_duration = self._get_avg_session_duration(sessions, session_count)
|
||||||
|
|
||||||
|
chart_data, chart_tooltip_format, chart_granularity = self._get_chart_data(
|
||||||
|
sessions, hits, start_time, end_time, tz_now
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"currently_online": currently_online,
|
||||||
|
"session_count": session_count,
|
||||||
|
"hit_count": hit_count,
|
||||||
|
"has_hits": has_hits,
|
||||||
|
"bounce_rate_pct": bounce_count * 100 / session_count
|
||||||
|
if session_count > 0
|
||||||
|
else None,
|
||||||
|
"avg_session_duration": avg_session_duration,
|
||||||
|
"avg_load_time": avg_load_time,
|
||||||
|
"avg_hits_per_session": avg_hits_per_session,
|
||||||
|
"locations": locations,
|
||||||
|
"referrers": referrers,
|
||||||
|
"countries": countries,
|
||||||
|
"operating_systems": operating_systems,
|
||||||
|
"browsers": browsers,
|
||||||
|
"devices": devices,
|
||||||
|
"device_types": device_types,
|
||||||
|
"chart_data": chart_data,
|
||||||
|
"chart_tooltip_format": chart_tooltip_format,
|
||||||
|
"chart_granularity": chart_granularity,
|
||||||
|
"online": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_avg_session_duration(self, sessions, session_count):
|
||||||
try:
|
try:
|
||||||
avg_session_duration = sessions.annotate(
|
avg_session_duration = sessions.annotate(
|
||||||
duration=models.F("last_seen") - models.F("start_time")
|
duration=models.F("last_seen") - models.F("start_time")
|
||||||
@ -212,68 +242,72 @@ class Service(models.Model):
|
|||||||
if session_count == 0:
|
if session_count == 0:
|
||||||
avg_session_duration = None
|
avg_session_duration = None
|
||||||
|
|
||||||
|
return avg_session_duration
|
||||||
|
|
||||||
|
def _get_chart_data(self, sessions, hits, start_time, end_time, tz_now):
|
||||||
# Show hourly chart for date ranges of 3 days or less, otherwise daily chart
|
# Show hourly chart for date ranges of 3 days or less, otherwise daily chart
|
||||||
if (end_time - start_time).days < 3:
|
if (end_time - start_time).days < 3:
|
||||||
session_chart_tooltip_format = "MM/dd HH:mm"
|
chart_tooltip_format = "MM/dd HH:mm"
|
||||||
session_chart_granularity = "hourly"
|
chart_granularity = "hourly"
|
||||||
session_chart_data = {
|
sessions_per_hour = (
|
||||||
k["hour"]: k["count"]
|
sessions.annotate(hour=TruncHour("start_time"))
|
||||||
for k in sessions.annotate(hour=TruncHour("start_time"))
|
|
||||||
.values("hour")
|
.values("hour")
|
||||||
.annotate(count=models.Count("uuid"))
|
.annotate(count=models.Count("uuid"))
|
||||||
.order_by("hour")
|
.order_by("hour")
|
||||||
|
)
|
||||||
|
chart_data = {
|
||||||
|
k["hour"]: {"sessions": k["count"]} for k in sessions_per_hour
|
||||||
}
|
}
|
||||||
for hour_offset in range(int((end_time - start_time).total_seconds() / 3600) + 1):
|
hits_per_hour = (
|
||||||
hour = (start_time + timezone.timedelta(hours=hour_offset))
|
hits.annotate(hour=TruncHour("start_time"))
|
||||||
if hour not in session_chart_data:
|
.values("hour")
|
||||||
session_chart_data[hour] = 0 if hour <= tz_now else None
|
.annotate(count=models.Count("id"))
|
||||||
|
.order_by("hour")
|
||||||
|
)
|
||||||
|
for k in hits_per_hour:
|
||||||
|
if k["hour"] not in chart_data:
|
||||||
|
chart_data[k["hour"]] = {"hits": k["count"], "sessions": 0}
|
||||||
|
else:
|
||||||
|
chart_data[k["hour"]]["hits"] = k["count"]
|
||||||
|
|
||||||
|
hours_range = range(int((end_time - start_time).total_seconds() / 3600) + 1)
|
||||||
|
for hour_offset in hours_range:
|
||||||
|
hour = start_time + timezone.timedelta(hours=hour_offset)
|
||||||
|
if hour not in chart_data and hour <= tz_now:
|
||||||
|
chart_data[hour] = {"sessions": 0, "hits": 0}
|
||||||
else:
|
else:
|
||||||
session_chart_tooltip_format = "MMM d"
|
chart_tooltip_format = "MMM d"
|
||||||
session_chart_granularity = "daily"
|
chart_granularity = "daily"
|
||||||
session_chart_data = {
|
sessions_per_day = (
|
||||||
k["date"]: k["count"]
|
sessions.annotate(date=TruncDate("start_time"))
|
||||||
for k in sessions.annotate(date=TruncDate("start_time"))
|
|
||||||
.values("date")
|
.values("date")
|
||||||
.annotate(count=models.Count("uuid"))
|
.annotate(count=models.Count("uuid"))
|
||||||
.order_by("date")
|
.order_by("date")
|
||||||
}
|
)
|
||||||
|
chart_data = {k["date"]: {"sessions": k["count"]} for k in sessions_per_day}
|
||||||
|
hits_per_day = (
|
||||||
|
hits.annotate(date=TruncDate("start_time"))
|
||||||
|
.values("date")
|
||||||
|
.annotate(count=models.Count("id"))
|
||||||
|
.order_by("date")
|
||||||
|
)
|
||||||
|
for k in hits_per_day:
|
||||||
|
chart_data[k["date"]]["hits"] = k["count"]
|
||||||
|
|
||||||
for day_offset in range((end_time - start_time).days + 1):
|
for day_offset in range((end_time - start_time).days + 1):
|
||||||
day = (start_time + timezone.timedelta(days=day_offset)).date()
|
day = (start_time + timezone.timedelta(days=day_offset)).date()
|
||||||
if day not in session_chart_data:
|
if day not in chart_data and day <= tz_now.date():
|
||||||
session_chart_data[day] = 0 if day <= tz_now.date() else None
|
chart_data[day] = {"sessions": 0, "hits": 0}
|
||||||
|
|
||||||
return {
|
chart_data = sorted(chart_data.items(), key=lambda k: k[0])
|
||||||
"currently_online": currently_online,
|
chart_data = {
|
||||||
"session_count": session_count,
|
'sessions': [v['sessions'] for k, v in chart_data],
|
||||||
"hit_count": hit_count,
|
'hits': [v['hits'] for k, v in chart_data],
|
||||||
"has_hits": has_hits,
|
'labels': [str(k) for k, v in chart_data],
|
||||||
"avg_hits_per_session": hit_count / (max(session_count, 1)),
|
|
||||||
"bounce_rate_pct": bounce_count * 100 / session_count
|
|
||||||
if session_count > 0
|
|
||||||
else None,
|
|
||||||
"avg_session_duration": avg_session_duration,
|
|
||||||
"avg_load_time": avg_load_time,
|
|
||||||
"avg_hits_per_session": avg_hits_per_session,
|
|
||||||
"locations": locations,
|
|
||||||
"referrers": referrers,
|
|
||||||
"countries": countries,
|
|
||||||
"operating_systems": operating_systems,
|
|
||||||
"browsers": browsers,
|
|
||||||
"devices": devices,
|
|
||||||
"device_types": device_types,
|
|
||||||
"session_chart_data": json.dumps(
|
|
||||||
[
|
|
||||||
{"x": str(key), "y": value}
|
|
||||||
for key, value in sorted(
|
|
||||||
session_chart_data.items(), key=lambda k: k[0]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
"session_chart_tooltip_format": session_chart_tooltip_format,
|
|
||||||
"session_chart_granularity": session_chart_granularity,
|
|
||||||
"online": True,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return chart_data, chart_tooltip_format, chart_granularity
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return reverse(
|
||||||
"dashboard:service",
|
"dashboard:service",
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<hr class="sep h-4">
|
<hr class="sep h-4">
|
||||||
<div style="bottom: -1px;">
|
<div style="bottom: -1px;">
|
||||||
{% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data sparkline=True height=100 name=object.uuid tooltip_format=stats.session_chart_tooltip_format granularity=stats.session_chart_granularity %}
|
{% include 'dashboard/includes/time_chart.html' with data=stats.chart_data sparkline=True height=100 name=object.uuid tooltip_format=stats.chart_tooltip_format granularity=stats.chart_granularity %}
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -5,12 +5,14 @@
|
|||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
shared: false,
|
shared: true,
|
||||||
x: {
|
x: {
|
||||||
format: '{{tooltip_format|default:"MMM d"}}',
|
format: '{{tooltip_format|default:"MMM d"}}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
colors: ["#805AD5"],
|
legend: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
chart: {
|
chart: {
|
||||||
zoom: {
|
zoom: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -18,7 +20,7 @@
|
|||||||
toolbar: {
|
toolbar: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
type: 'area',
|
type: 'line',
|
||||||
height: {{height|default:"200"}},
|
height: {{height|default:"200"}},
|
||||||
offsetY: -1,
|
offsetY: -1,
|
||||||
animations: {
|
animations: {
|
||||||
@ -27,20 +29,10 @@
|
|||||||
sparkline: {
|
sparkline: {
|
||||||
enabled: {% if sparkline %}true{% else %}false{% endif %},
|
enabled: {% if sparkline %}true{% else %}false{% endif %},
|
||||||
},
|
},
|
||||||
fill: {
|
|
||||||
type: 'gradient',
|
|
||||||
gradient: {
|
|
||||||
shadeIntensity: 1,
|
|
||||||
inverseColors: false,
|
|
||||||
opacityFrom: 0.8,
|
|
||||||
opacityTo: 0,
|
|
||||||
stops: [0, 75, 100]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{% if granularity == "daily" and click_zoom %}
|
{% if granularity == "daily" and click_zoom %}
|
||||||
events: {
|
events: {
|
||||||
markerClick: function(event, chartContext, { seriesIndex, dataPointIndex, w: {config}}) {
|
markerClick: function(event, chartContext, { seriesIndex, dataPointIndex, w: {config}}) {
|
||||||
const day = config.series[seriesIndex].data[dataPointIndex].x
|
const day = config.labels[dataPointIndex]
|
||||||
window.location.href = `?startDate=${day}&endDate=${day}`
|
window.location.href = `?startDate=${day}&endDate=${day}`
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -79,13 +71,22 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
stroke: {
|
stroke: {
|
||||||
width: 1.5,
|
width: 2,
|
||||||
|
curve: 'smooth',
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
name: "{{unit|default:'Sessions'}}",
|
name: "Hits",
|
||||||
data: {{data|safe}}
|
type: 'area',
|
||||||
}]
|
color: "#ddd6fe",
|
||||||
|
data: {{data.hits|safe}}
|
||||||
|
}, {
|
||||||
|
name: "Sessions",
|
||||||
|
type: 'line',
|
||||||
|
color: "#805AD5",
|
||||||
|
data: {{data.sessions|safe}}
|
||||||
|
}],
|
||||||
|
labels: {{data.labels|safe}}
|
||||||
};
|
};
|
||||||
var triggerMatchesChart = new ApexCharts(document.querySelector("#chart{{name|default:'Main'}}"), triggerMatchesChartOptions);
|
var triggerMatchesChart = new ApexCharts(document.querySelector("#chart{{name|default:'Main'}}"), triggerMatchesChartOptions);
|
||||||
triggerMatchesChart.render();
|
triggerMatchesChart.render();
|
||||||
</script>
|
</script>
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
<div class="card overflow-visible ~neutral !low py-0 mb-6">
|
<div class="card overflow-visible ~neutral !low py-0 mb-6">
|
||||||
{% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data tooltip_format=stats.session_chart_tooltip_format granularity=stats.session_chart_granularity click_zoom=True %}
|
{% include 'dashboard/includes/time_chart.html' with data=stats.chart_data tooltip_format=stats.chart_tooltip_format granularity=stats.chart_granularity click_zoom=True %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||||||
|
Loading…
Reference in New Issue
Block a user