Skip to content

Commit

Permalink
Add statistics page (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
anorthall committed Mar 31, 2023
1 parent bc007db commit b24e4aa
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 3 deletions.
46 changes: 44 additions & 2 deletions app/logger/statistics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
from .models import Trip
import humanize
from django.db.models import Count
from django.contrib.gis.measure import Distance
from django.utils import timezone
import humanize
from .models import Trip


def sort_comma_separated_list(qs, value, limit=10):
"""Sort a field that has a comma separated list of values from a QuerySet"""
values = qs.values(value)
common = {}
for v in values:
split_list = v[value].split(",")
for v in split_list:
trimmed = v.strip()
if not trimmed:
continue
if v in common:
common[trimmed] += 1
else:
common[trimmed] = 1
return sorted(common.items(), key=lambda x: x[1], reverse=True)[0:limit]


def stats_for_user(qs, year=None):
Expand Down Expand Up @@ -45,3 +63,27 @@ def stats_for_user(qs, year=None):
)

return results


def common_caves(qs, limit=10):
"""Get a list of the most common caves in a QuerySet"""
return (
qs.values("cave_name")
.annotate(count=Count("cave_name"))
.order_by("-count")[0:limit]
)


def common_cavers(qs, limit=10):
"""Get a list of the most common cavers in a QuerySet"""
return sort_comma_separated_list(qs, "cavers", limit)


def common_clubs(qs, limit=10):
"""Get a list of the most common clubs in a QuerySet"""
return sort_comma_separated_list(qs, "clubs", limit)


def common_types(qs, limit=10):
"""Get a list of the most common types in a QuerySet"""
return qs.values("type").annotate(count=Count("type")).order_by("-count")[0:limit]
2 changes: 1 addition & 1 deletion app/logger/templates/logger/trip_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
</div>
{% else %}
<div class="card-body">
<p class="lead mb-0">
<p class="m-0">
You have not logged any trips yet! Why not <a href="{% url 'log:trip_create' %}">add one now?</a>
</p>
</div>
Expand Down
187 changes: 187 additions & 0 deletions app/logger/templates/statistics.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
{% extends "base_sidebar.html" %}
{% load get %}
{% load distformat %}
{% block title %}Your statistics{% endblock %}
{% block sidebar %}{% include "sidebar_trips.html" %}{% endblock %}

{% block main %}

{% if trips %}
<div class="card">
<div class="card-header">
Distance and time
</div>

<div class="card-body p-0">
<div class="row">
<div class="table-responsive">
<table class="table table-borderless text-center">
<thead>
<tr class="border-bottom">
<th scope="col"></th>
<th scope="col">{{ year0 }}</th>
<th scope="col">{{ year1 }}</th>
<th scope="col">{{ year2 }}</th>
<th scope="col">Total</th>
</tr>
</thead>

<tbody>
<tr>
<th scope="row">Rope ascent</th>
<td>{{ trip_stats_year0.vert_up|distformat:dist_format }}</td>
<td>{{ trip_stats_year1.vert_up|distformat:dist_format }}</td>
<td>{{ trip_stats_year2.vert_up|distformat:dist_format }}</td>
<td>{{ trip_stats.vert_up|distformat:dist_format }}</td>
</tr>

<tr>
<th scope="row">Rope descent</th>
<td>{{ trip_stats_year0.vert_down|distformat:dist_format }}</td>
<td>{{ trip_stats_year1.vert_down|distformat:dist_format }}</td>
<td>{{ trip_stats_year2.vert_down|distformat:dist_format }}</td>
<td>{{ trip_stats.vert_down|distformat:dist_format }}</td>
</tr>

<tr>
<th scope="row">Surveyed</th>
<td>{{ trip_stats_year0.surveyed|distformat:dist_format }}</td>
<td>{{ trip_stats_year1.surveyed|distformat:dist_format }}</td>
<td>{{ trip_stats_year2.surveyed|distformat:dist_format }}</td>
<td>{{ trip_stats.surveyed|distformat:dist_format }}</td>
</tr>

<tr>
<th scope="row">Resurveyed</th>
<td>{{ trip_stats_year0.resurveyed|distformat:dist_format }}</td>
<td>{{ trip_stats_year1.resurveyed|distformat:dist_format }}</td>
<td>{{ trip_stats_year2.resurveyed|distformat:dist_format }}</td>
<td>{{ trip_stats.resurveyed|distformat:dist_format }}</td>
</tr>

<tr>
<th scope="row">Horizontal</th>
<td>{{ trip_stats_year0.horizontal|distformat:dist_format }}</td>
<td>{{ trip_stats_year1.horizontal|distformat:dist_format }}</td>
<td>{{ trip_stats_year2.horizontal|distformat:dist_format }}</td>
<td>{{ trip_stats.horizontal|distformat:dist_format }}</td>
</tr>

<tr>
<th scope="row">Aid climbed</th>
<td>{{ trip_stats_year0.aided|distformat:dist_format }}</td>
<td>{{ trip_stats_year1.aided|distformat:dist_format }}</td>
<td>{{ trip_stats_year2.aided|distformat:dist_format }}</td>
<td>{{ trip_stats.aided|distformat:dist_format }}</td>
</tr>

<tr class="border-top">
<th scope="row">Total trips</th>
<td>{{ trip_stats_year0.trips }}</td>
<td>{{ trip_stats_year1.trips }}</td>
<td>{{ trip_stats_year2.trips }}</td>
<td>{{ trip_stats.trips }}</td>
</tr>

<tr>
<th scope="row">
Time
</th>
<td>{{ trip_stats_year0.time }}</th>
<td>{{ trip_stats_year1.time }}</th>
<td>{{ trip_stats_year2.time }}</th>
<td>{{ trip_stats.time }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

<div class="card mt-4">
<div class="card-header">
Most common
</div>

<div class="card-body">
<div class="row g-3">
<div class="col-12 col-lg-6">
<h5>Most common caves</h5>
{% if common_caves %}
<ol>
{% for cave in common_caves %}
<li>
<strong>{{ cave.cave_name }}</strong> &mdash; {{ cave.count }} trip{{ cave.count|pluralize }}
</li>
{% endfor %}
</ol>
{% else %}
<p>Sorry, no data for this!</p>
{% endif %}
</div>

<div class="col-12 col-lg-6">
<h5>Most common cavers</h5>
{% if common_cavers %}
<ol>
{% for caver, count in common_cavers %}
<li>
<strong>{{ caver }}</strong> &mdash; {{ count }} trip{{ count|pluralize }}
</li>
{% endfor %}
</ol>
{% else %}
<p>Sorry, no data for this!</p>
{% endif %}
</div>

<div class="col-12 col-lg-6">
<h5>Most common trip types</h5>
{% if common_types %}
<ol>
{% for type in common_types %}
<li>
<strong>{{ type.type }}</strong> &mdash; {{ type.count }} trip{{ type.count|pluralize }}
</li>
{% endfor %}
</ol>
{% else %}
<p>Sorry, no data for this!</p>
{% endif %}
</div>

<div class="col-12 col-lg-6">
<h5>Most common clubs</h5>
{% if common_clubs %}
<ol>
{% for club, count in common_clubs %}
<li>
<strong>{{ club }}</strong> &mdash; {{ count }} trip{{ count|pluralize }}
</li>
{% endfor %}
</ol>
{% else %}
<p>Sorry, no data for this!</p>
{% endif %}
</div>
</div>
</div>
</div>
{% else %}

<div class="card">
<div class="card-header fs-4">
Statistics
</div>

<div class="card-body">
<p class="m-0">
You have not logged any trips yet! Why not <a href="{% url 'log:trip_create' %}">add one now?</a>
</p>
</div>
</div>

{% endif %}

{% endblock main %}
1 change: 1 addition & 0 deletions app/logger/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
views.ReportDeleteView.as_view(),
name="report_delete",
),
path("statistics/", views.user_statistics, name="statistics"),
path("about/", views.about, name="about"),
path("admin-tools/", views.admin_tools, name="admin_tools"),
]
33 changes: 33 additions & 0 deletions app/logger/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,39 @@ def export(request):
return response # Return the CSV file as a HttpResponse


@login_required
def user_statistics(request):
"""Show statistics for a user."""
trips = request.user.trips

# Generate stats for trips/distances by year
this_year = timezone.now().year
prev_year = (timezone.now() - timezone.timedelta(days=365)).year
prev_year_2 = (timezone.now() - timezone.timedelta(days=730)).year
trip_stats = statistics.stats_for_user(trips)
trip_stats_year0 = statistics.stats_for_user(trips, year=prev_year_2)
trip_stats_year1 = statistics.stats_for_user(trips, year=prev_year)
trip_stats_year2 = statistics.stats_for_user(trips, year=this_year)

context = {
"trips": trips,
"user": request.user,
"dist_format": request.user.units,
"year0": prev_year_2,
"year1": prev_year,
"year2": this_year,
"trip_stats": trip_stats,
"trip_stats_year0": trip_stats_year0,
"trip_stats_year1": trip_stats_year1,
"trip_stats_year2": trip_stats_year2,
"common_caves": statistics.common_caves(trips),
"common_cavers": statistics.common_cavers(trips),
"common_types": statistics.common_types(trips),
"common_clubs": statistics.common_clubs(trips),
}
return render(request, "statistics.html", context)


def admin_tools(request):
"""Tools for website administrators."""
if not request.user.is_superuser:
Expand Down
4 changes: 4 additions & 0 deletions app/templates/sidebar_core.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<i class="bi bi-list"></i>&nbsp; Trips
</a>

<a class="nav-link {% active_link 'log:statistics' %}" href="{% url 'log:statistics' %}">
<i class="bi bi-bar-chart-line-fill"></i>&nbsp; Statistics
</a>

<a class="nav-link {% active_link 'users:profile' %}" href="{% url 'users:profile' %}">
<i class="bi bi-person-circle"></i>&nbsp; Account
</a>
Expand Down

0 comments on commit b24e4aa

Please sign in to comment.