Skip to content

Commit

Permalink
feat(stats): ajout de la vue historique des visiteurs mensuels (#656)
Browse files Browse the repository at this point in the history
## Description

🎸 Ajout de la vue historique des visiteurs mensuels, en complément du
funnel de la vue `statistiques`
🎸 Ajout des liens de navigation entre la vue `monthly_visitors` et la
vue `statistiques`

## Type de changement

🎢 Nouvelle fonctionnalité (changement non cassant qui ajoute une
fonctionnalité).

### Captures d'écran (optionnel)

![image](https://github.com/gip-inclusion/itou-communaute-django/assets/11419273/5d37676d-1c0f-492d-8455-fa06bf6c91fc)

lien cliquable sur la page statistiques principale


![image](https://github.com/gip-inclusion/itou-communaute-django/assets/11419273/88cc0e45-f810-4f5c-9e91-e9e561985de7)
  • Loading branch information
vincentporte authored Jun 6, 2024
1 parent 6f3049a commit 804be3c
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 3 deletions.
53 changes: 53 additions & 0 deletions lacommunaute/forum_stats/tests/tests_views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from dateutil.relativedelta import relativedelta
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from django.utils.dateformat import format
from django.utils.timezone import localdate
from faker import Faker
from machina.core.loading import get_class
from pytest_django.asserts import assertContains

from lacommunaute.forum_stats.enums import Period
from lacommunaute.forum_stats.factories import StatFactory
Expand Down Expand Up @@ -110,3 +112,54 @@ def test_impact_in_context_data(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["impact"]["date"][0], today.strftime("%Y-%m-%d"))
self.assertEqual(response.context["impact"]["nb_uniq_visitors_returning"][0], 1)

def test_navigation(self):
url = reverse("forum_stats:statistiques")
response = self.client.get(url)
self.assertContains(response, "<a href=/statistiques/monthly-visitors/>")


class TestMonthlyVisitorsView:
def test_context_data(self, client, db):
url = reverse("forum_stats:monthly_visitors")
today = localdate()
empty_res = {
"date": [],
"nb_uniq_visitors": [],
"nb_uniq_active_visitors": [],
"nb_uniq_engaged_visitors": [],
"nb_uniq_visitors_returning": [],
}

# no data
response = client.get(url)
assert response.status_code == 200
assertContains(response, "Utilisateurs uniques mensuels")
assert response.context["monthly_visitors"] == empty_res

# undesired data
StatFactory(name="nb_uniq_visitors_returning", period=Period.DAY, date=today)
StatFactory(name=faker.word(), period=Period.MONTH, date=today)
StatFactory(name="nb_uniq_visitors", period=Period.MONTH, date=today - relativedelta(months=9), value=1)
response = client.get(url)
assert response.status_code == 200
assert response.context["monthly_visitors"] == empty_res

# expected data
StatFactory(name="nb_uniq_visitors_returning", period=Period.MONTH, date=today, value=2)
StatFactory(name="nb_uniq_visitors", period=Period.MONTH, date=today - relativedelta(months=8), value=10)
response = client.get(url)
assert response.status_code == 200
assert response.context["monthly_visitors"] == {
"date": [(today - relativedelta(months=8)).strftime("%Y-%m-%d"), today.strftime("%Y-%m-%d")],
"nb_uniq_visitors": [10],
"nb_uniq_active_visitors": [],
"nb_uniq_engaged_visitors": [],
"nb_uniq_visitors_returning": [2],
}

def test_navigation(self, client, db):
url = reverse("forum_stats:monthly_visitors")
response = client.get(url)
assert response.status_code == 200
assertContains(response, '<a href="/statistiques/">retour vers la page statistiques</a>')
7 changes: 5 additions & 2 deletions lacommunaute/forum_stats/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from django.urls import path

from lacommunaute.forum_stats.views import StatistiquesPageView
from lacommunaute.forum_stats.views import MonthlyVisitorsViews, StatistiquesPageView


app_name = "forum_stats"

urlpatterns = [path("", StatistiquesPageView.as_view(), name="statistiques")]
urlpatterns = [
path("", StatistiquesPageView.as_view(), name="statistiques"),
path("monthly-visitors/", MonthlyVisitorsViews.as_view(), name="monthly_visitors"),
]
27 changes: 27 additions & 0 deletions lacommunaute/forum_stats/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import logging

from dateutil.relativedelta import relativedelta
from django.db.models import CharField
from django.db.models.functions import Cast
from django.utils import timezone
from django.utils.dateformat import format
from django.utils.timezone import localdate
from django.views.generic.base import TemplateView

from lacommunaute.forum_stats.models import Stat
Expand Down Expand Up @@ -71,3 +73,28 @@ def get_context_data(self, **kwargs):
context = {**context, **self.get_funnel_data()}

return context


class MonthlyVisitorsViews(TemplateView):
template_name = "forum_stats/monthly_visitors.html"

def get_monthly_visitors(self):
indicator_names = [
"nb_uniq_visitors",
"nb_uniq_active_visitors",
"nb_uniq_engaged_visitors",
"nb_uniq_visitors_returning",
]
datas = (
Stat.objects.filter(
period="month", name__in=indicator_names, date__gt=localdate() - relativedelta(months=9)
)
.values("name", "value")
.annotate(date=Cast("date", CharField()))
)
return extract_values_in_list(datas, indicator_names)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["monthly_visitors"] = self.get_monthly_visitors()
return context
80 changes: 80 additions & 0 deletions lacommunaute/templates/forum_stats/monthly_visitors.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{% extends "layouts/base.html" %}
{% load str_filters %}
{% load js_filters %}
{% load static %}
{% block title %}Statistiques{{ block.super }}{% endblock %}
{% block body_class %}p-statistiques{{ block.super }}{% endblock %}
{% block content %}
<section class="s-title-01 mt-lg-5">
<div class="s-title-01__container container">
<div class="s-title-01__row row">
<div class="s-title-01__col col-12">
<h1 class="s-title-01__title h1">
<strong>Statistiques</strong>
</h1>
</div>
</div>
</div>
</section>
<section class="s-section">
<div class="s-section__container container">
<div class="s-section__row row">
<div class="s-section__col col-12">
<div class="c-box mb-3 mb-md-5">
<h2>Utilisateurs uniques mensuels</h2>
<canvas id="statChart"></canvas>
<p class="mt-1">
<small>
<a href="{% url 'forum_stats:statistiques' %}">retour vers la page statistiques</a>
</small>
</p>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script nonce="{{ request.csp_nonce }}">
const ctx_stats = document.getElementById('statChart');
new Chart(ctx_stats, {
type: 'line',
data: {
labels: {{ monthly_visitors.date | js }},
datasets: [
{
label: 'Utilisateurs',
data: {{ monthly_visitors.nb_uniq_visitors | js }},
borderColor: 'rgba(54, 162, 235)',
backgroundColor: 'rgba(154, 208, 245)',
},
{
label: 'Utilisateurs actifs',
data: {{ monthly_visitors.nb_uniq_active_visitors | js }},
borderColor: 'rgba(76, 120, 83)',
backgroundColor: 'rgba(112, 178, 123)',
},
{
label: 'Utilisateurs engagés',
data: {{ monthly_visitors.nb_uniq_engaged_visitors | js }},
borderColor: 'rgba(255, 159, 64)',
backgroundColor: 'rgba(255, 207, 159)',
},
{
label: 'Utilisateurs retour',
data: {{ monthly_visitors.nb_uniq_visitors_returning | js }},
borderColor: 'rgba(98, 42, 86)',
backgroundColor: 'rgba(155, 67, 136)',
},
]
},
options: {
responsive:true,
cubicInterpolationMode: 'monotone',
tension: 0.4,
}
});
</script>
{% endblock %}
4 changes: 3 additions & 1 deletion lacommunaute/templates/forum_stats/statistiques.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ <h2>Acquisition</h2>
<span class="h2 mb-3 text-white">{{ nb_uniq_engaged_visitors }}</span>
<span class="fs-sm">{{ engagement_percent }}% des util. actifs</span>
</div>
<figcaption class="fs-sm text-muted mt-3">Période : {{ period }}</figcaption>
<figcaption class="fs-sm text-muted mt-3">
Période : <a href={% url 'forum_stats:monthly_visitors' %}>{{ period }}</a>
</figcaption>
</figure>
</div>
<div class="col-12 col-lg-6">
Expand Down

0 comments on commit 804be3c

Please sign in to comment.