parent
7082a10805
commit
d843eb209d
1
src/agenda_culturel/static/cal-heatmap/README.md
Normal file
1
src/agenda_culturel/static/cal-heatmap/README.md
Normal file
@ -0,0 +1 @@
|
||||
next_month
|
1
src/agenda_culturel/static/cal-heatmap/cal-heatmap.css
Normal file
1
src/agenda_culturel/static/cal-heatmap/cal-heatmap.css
Normal file
@ -0,0 +1 @@
|
||||
.ch-container{display:block}.ch-container,.ch-domain,.ch-domain-container,.ch-domain-container-animation-wrapper{overflow:visible}.ch-domain-container.in-transition .ch-domain-container-animation-wrapper{overflow:hidden}.ch-domain-bg{fill:transparent}.ch-domain-text{fill:currentColor;font-size:10px}.ch-subdomain{overflow:visible}.ch-subdomain-bg{fill:#ededed}.ch-subdomain-bg.highlight{stroke:#444;stroke-width:1px}.ch-subdomain-bg:hover{stroke:#000;stroke-width:1px}.ch-subdomain-text{font-size:8px;pointer-events:none}[data-theme=dark] .ch-subdomain-bg{fill:#2d333b}[data-theme=dark] .ch-subdomain-bg.highlight{stroke:#768390}[data-theme=dark] .ch-subdomain-bg:hover{stroke:#636e7b}#ch-plugin-legend>svg{background:transparent;color:currentColor}#ch-tooltip{background:#222;border-radius:2px;box-shadow:2px 2px 2px rgba(0,0,0,.2);box-sizing:border-box;color:#bbb;display:none;font-size:12px;line-height:1.4;padding:5px 10px;text-align:center}#ch-tooltip[data-show]{display:block}#ch-tooltip-arrow,#ch-tooltip-arrow:before{background:inherit;height:8px;position:absolute;width:8px}#ch-tooltip-arrow{visibility:hidden}#ch-tooltip-arrow:before{content:"";transform:rotate(45deg);visibility:visible}#ch-tooltip[data-popper-placement^=top]>#ch-tooltip-arrow{bottom:-4px;margin-left:-4px}#ch-tooltip[data-popper-placement^=bottom]>#ch-tooltip-arrow{margin-left:-4px;top:-4px}#ch-tooltip[data-popper-placement^=left]>#ch-tooltip-arrow{right:-4px}#ch-tooltip[data-popper-placement^=right]>#ch-tooltip-arrow{left:-4px}#ch-tooltip[data-theme=dark]{background:#636e7b;color:#cdd9e5}
|
13211
src/agenda_culturel/static/cal-heatmap/cal-heatmap.esm.js
Normal file
13211
src/agenda_culturel/static/cal-heatmap/cal-heatmap.esm.js
Normal file
File diff suppressed because one or more lines are too long
13214
src/agenda_culturel/static/cal-heatmap/cal-heatmap.js
Normal file
13214
src/agenda_culturel/static/cal-heatmap/cal-heatmap.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/agenda_culturel/static/cal-heatmap/cal-heatmap.min.js
vendored
Normal file
1
src/agenda_culturel/static/cal-heatmap/cal-heatmap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3793
src/agenda_culturel/static/cal-heatmap/plugins/CalendarLabel.esm.js
Normal file
3793
src/agenda_culturel/static/cal-heatmap/plugins/CalendarLabel.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
3801
src/agenda_culturel/static/cal-heatmap/plugins/CalendarLabel.js
Normal file
3801
src/agenda_culturel/static/cal-heatmap/plugins/CalendarLabel.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/agenda_culturel/static/cal-heatmap/plugins/CalendarLabel.min.js
vendored
Normal file
1
src/agenda_culturel/static/cal-heatmap/plugins/CalendarLabel.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4521
src/agenda_culturel/static/cal-heatmap/plugins/Legend.esm.js
Normal file
4521
src/agenda_culturel/static/cal-heatmap/plugins/Legend.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
4526
src/agenda_culturel/static/cal-heatmap/plugins/Legend.js
Normal file
4526
src/agenda_culturel/static/cal-heatmap/plugins/Legend.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/agenda_culturel/static/cal-heatmap/plugins/Legend.min.js
vendored
Normal file
1
src/agenda_culturel/static/cal-heatmap/plugins/Legend.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6128
src/agenda_culturel/static/cal-heatmap/plugins/LegendLite.esm.js
Normal file
6128
src/agenda_culturel/static/cal-heatmap/plugins/LegendLite.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
6133
src/agenda_culturel/static/cal-heatmap/plugins/LegendLite.js
Normal file
6133
src/agenda_culturel/static/cal-heatmap/plugins/LegendLite.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/agenda_culturel/static/cal-heatmap/plugins/LegendLite.min.js
vendored
Normal file
1
src/agenda_culturel/static/cal-heatmap/plugins/LegendLite.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3768
src/agenda_culturel/static/cal-heatmap/plugins/Tooltip.esm.js
Normal file
3768
src/agenda_culturel/static/cal-heatmap/plugins/Tooltip.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
3774
src/agenda_culturel/static/cal-heatmap/plugins/Tooltip.js
Normal file
3774
src/agenda_culturel/static/cal-heatmap/plugins/Tooltip.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/agenda_culturel/static/cal-heatmap/plugins/Tooltip.min.js
vendored
Normal file
1
src/agenda_culturel/static/cal-heatmap/plugins/Tooltip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
.ch-container{display:block}.ch-container,.ch-domain,.ch-domain-container,.ch-domain-container-animation-wrapper{overflow:visible}.ch-domain-container.in-transition .ch-domain-container-animation-wrapper{overflow:hidden}.ch-domain-bg{fill:transparent}.ch-domain-text{fill:currentColor;font-size:10px}.ch-subdomain{overflow:visible}.ch-subdomain-bg{fill:#ededed}.ch-subdomain-bg.highlight{stroke:#444;stroke-width:1px}.ch-subdomain-bg:hover{stroke:#000;stroke-width:1px}.ch-subdomain-text{font-size:8px;pointer-events:none}[data-theme=dark] .ch-subdomain-bg{fill:#2d333b}[data-theme=dark] .ch-subdomain-bg.highlight{stroke:#768390}[data-theme=dark] .ch-subdomain-bg:hover{stroke:#636e7b}#ch-plugin-legend>svg{background:transparent;color:currentColor}#ch-tooltip{background:#222;border-radius:2px;box-shadow:2px 2px 2px rgba(0,0,0,.2);box-sizing:border-box;color:#bbb;display:none;font-size:12px;line-height:1.4;padding:5px 10px;text-align:center}#ch-tooltip[data-show]{display:block}#ch-tooltip-arrow,#ch-tooltip-arrow:before{background:inherit;height:8px;position:absolute;width:8px}#ch-tooltip-arrow{visibility:hidden}#ch-tooltip-arrow:before{content:"";transform:rotate(45deg);visibility:visible}#ch-tooltip[data-popper-placement^=top]>#ch-tooltip-arrow{bottom:-4px;margin-left:-4px}#ch-tooltip[data-popper-placement^=bottom]>#ch-tooltip-arrow{margin-left:-4px;top:-4px}#ch-tooltip[data-popper-placement^=left]>#ch-tooltip-arrow{right:-4px}#ch-tooltip[data-popper-placement^=right]>#ch-tooltip-arrow{left:-4px}#ch-tooltip[data-theme=dark]{background:#636e7b;color:#cdd9e5}
|
2
src/agenda_culturel/static/js/d3.v7.min.js
vendored
Normal file
2
src/agenda_culturel/static/js/d3.v7.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
src/agenda_culturel/static/js/popper.min.js
vendored
Normal file
6
src/agenda_culturel/static/js/popper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1906,3 +1906,7 @@ dialog {
|
||||
.single-event header .buttons [role="button"] {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
#cal-heatmap-startday g {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -112,26 +112,18 @@
|
||||
|
||||
<footer class="container-fluid">
|
||||
<div class="grid">
|
||||
|
||||
<div>
|
||||
<a href="{% url 'mentions_legales' %}">{% picto_from_name "align-left" %} mentions légales</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'view_places' %}">{% picto_from_name "map-pin" %} lieux</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'view_organisations' %}">{% picto_from_name "users" %} organisateurs</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'view_all_tags' %}">{% picto_from_name "tag" %} étiquettes</a>
|
||||
<a href="{% url 'view_places' %}">{% picto_from_name "map-pin" %} lieux</a><br>
|
||||
<a href="{% url 'view_organisations' %}">{% picto_from_name "users" %} organisateurs</a><br>
|
||||
<a href="{% url 'view_all_tags' %}">{% picto_from_name "tag" %} étiquettes</a><br>
|
||||
<a href="{% url 'statistics' %}">{% picto_from_name "pie-chart" %} statistiques</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href="{% url 'about' %}">{% picto_from_name "users" %} à propos</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'contact' %}">{% picto_from_name "mail" %} contact</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'about' %}">{% picto_from_name "users" %} à propos</a><br>
|
||||
<a href="{% url 'mentions_legales' %}">{% picto_from_name "align-left" %} mentions légales</a><br>
|
||||
<a href="{% url 'contact' %}">{% picto_from_name "mail" %} contact</a><br>
|
||||
{% if user.is_authenticated %}
|
||||
<a href="{% url 'administration' %}">{% picto_from_name "settings" %} administrer</a>
|
||||
<p>Vous êtes connecté(e) en tant que {{ user }}</p>
|
||||
|
127
src/agenda_culturel/templates/agenda_culturel/statistics.html
Normal file
127
src/agenda_culturel/templates/agenda_culturel/statistics.html
Normal file
@ -0,0 +1,127 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
{% load utils_extra %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% block og_title %}Statistiques{% endblock %}{% endblock %}
|
||||
|
||||
{% load cat_extra %}
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
<script src="{% static 'js/d3.v7.min.js' %}"></script>
|
||||
<script src="{% static 'cal-heatmap/cal-heatmap.min.js' %}"></script>
|
||||
<script src="{% static 'cal-heatmap/plugins/CalendarLabel.min.js' %}"></script>
|
||||
<script src="{% static 'js/popper.min.js' %}"></script>
|
||||
<script src="{% static 'cal-heatmap/plugins/Tooltip.min.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'cal-heatmap/cal-heatmap.css' %}">
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<header><h1>Statistiques</h1></header>
|
||||
<p>On retrouve sur cette page une synthèse des événements publiés sur l'agenda culturel pommes de lune.</p>
|
||||
|
||||
<h2>Par date de l'événement</h2>
|
||||
<p>Pour chaque date, on retrouve le nombre d'événements qui débutent à cette date (sauf événements récurrents).</p>
|
||||
<div class="large-table">
|
||||
<div id="cal-heatmap-startday"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Par jour de création sur l'agenda</h2>
|
||||
<p>Pour chaque date, on retrouve le nombre d'événements qui ont été créé ce jour-là, par import automatique ou par création manuelle.
|
||||
Ce nombre ne tient pas en compte les événements qui sont des doublons d'événements déjà importés.
|
||||
</p>
|
||||
<div class="large-table">
|
||||
<div id="cal-heatmap-creation"></div>
|
||||
</div>
|
||||
|
||||
<h2>Par ville</h2>
|
||||
<p>Nombre d'événements référencés par ville.</p>
|
||||
<ul>
|
||||
{% for v in nb_by_city %}
|
||||
<li><strong>{{ v.city }} :</strong> {{ v.total }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</article>
|
||||
<script>
|
||||
// prepare data
|
||||
const data_startday = [
|
||||
{% for s in stats_by_startday %}
|
||||
{% if not forloop.first %}, {% endif %}
|
||||
{date: new Date('{{ s.day.isoformat }}'), value: {{ s.total}}}
|
||||
{% endfor %}
|
||||
];
|
||||
const data_creation = [
|
||||
{% for s in stats_by_creation %}
|
||||
{% if not forloop.first %}, {% endif %}
|
||||
{date: new Date('{{ s.day.isoformat }}'), value: {{ s.total}}}
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
// define parameters
|
||||
const tooltip = {text: function (timestamp, value, dayjsDate) {
|
||||
if (value != null)
|
||||
return `${dayjsDate.format('DD/MM/YYYY')}: ${value}`;
|
||||
else
|
||||
return `${dayjsDate.format('DD/MM/YYYY')}`;
|
||||
}};
|
||||
const calendarlabel = { position: 'left', key: 'left', text: () => ['lun', '', 'mer', '', 'ven', '', 'dim'] };
|
||||
|
||||
// set options
|
||||
var options = {
|
||||
date: {
|
||||
locale: 'fr',
|
||||
start: new Date("{{ first_by_startday.isoformat }}"),
|
||||
highlight: new Date()
|
||||
},
|
||||
domain: {
|
||||
type: "month",
|
||||
label: {text: 'MMM YY'},
|
||||
padding: [3, 3, 3, 3]
|
||||
},
|
||||
subDomain: {
|
||||
type: "day",
|
||||
width: 20,
|
||||
height: 20,
|
||||
radius: 2,
|
||||
label : function (timestamp, value) { if (value == null) return ''; else return `${value}`; }
|
||||
},
|
||||
data: {
|
||||
source: data_startday,
|
||||
defaultValue: null,
|
||||
x: "date",
|
||||
y: "value"
|
||||
},
|
||||
theme: (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? "dark" : "light",
|
||||
itemSelector: '#cal-heatmap-startday',
|
||||
animationDuration: 0
|
||||
};
|
||||
|
||||
// create first heatmap
|
||||
const cal_startday = new CalHeatmap();
|
||||
cal_startday.paint(options, [
|
||||
[CalendarLabel, calendarlabel],
|
||||
[Tooltip, tooltip]
|
||||
]);
|
||||
|
||||
// create second heatmap
|
||||
options.itemSelector = '#cal-heatmap-creation';
|
||||
options.date.start = new Date("{{ first_by_creation.isoformat }}");
|
||||
options.data.source = data_creation;
|
||||
const cal_creation = new CalHeatmap();
|
||||
cal_creation.paint(options, [
|
||||
[CalendarLabel, calendarlabel],
|
||||
[Tooltip, tooltip]
|
||||
]);
|
||||
|
||||
cal_startday.on('click', (event, timestamp, value) => {
|
||||
const d = new Date(timestamp);
|
||||
const url = "{% url 'day_view' 0 0 0 %}".replace("/0/", "/" + d.getFullYear() + "/").replace("/0/", "/" + (d.getMonth() + 1) + "/").replace("/0/", "/" + d.getDate() + "/");
|
||||
window.location = url;
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -124,6 +124,7 @@ urlpatterns = [
|
||||
path("merci", thank_you, name="thank_you"),
|
||||
path("contact", MessageCreateView.as_view(), name="contact"),
|
||||
path("messages", view_messages, name="messages"),
|
||||
path("statistiques", statistics, name="statistics"),
|
||||
path("messages/spams/delete", delete_cm_spam, name="delete_cm_spam"),
|
||||
path(
|
||||
"message/<int:pk>",
|
||||
|
@ -16,7 +16,7 @@ from .utils import PlaceGuesser
|
||||
import hashlib
|
||||
from django.core.cache import cache
|
||||
from django.core.mail import mail_admins
|
||||
|
||||
import calendar as _calendar
|
||||
|
||||
from django.contrib.gis.geos import Point
|
||||
from django.contrib.gis.measure import D
|
||||
@ -78,7 +78,7 @@ from django.utils import timezone
|
||||
from django.utils.html import escape
|
||||
from datetime import date, timedelta
|
||||
from django.utils.timezone import datetime
|
||||
from django.db.models import Q, Subquery, OuterRef, Count, F, Func, BooleanField, ExpressionWrapper, When
|
||||
from django.db.models import Q, Subquery, OuterRef, Count, F, Func, BooleanField, ExpressionWrapper, When, Max
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -2269,6 +2269,45 @@ def view_tag(request, t, past=False):
|
||||
return render(request, "agenda_culturel/tag.html", context)
|
||||
|
||||
|
||||
def statistics(request):
|
||||
|
||||
|
||||
stats = {}
|
||||
max = {}
|
||||
first = {}
|
||||
last = {}
|
||||
|
||||
ev_published = Event.objects.filter(Q(status=Event.STATUS.PUBLISHED) &
|
||||
(Q(other_versions__isnull=True) |
|
||||
Q(other_versions__representative=F('pk')) |
|
||||
Q(other_versions__representative__isnull=True)))
|
||||
|
||||
for v in ['start_day', 'created_date__date']:
|
||||
after = 24
|
||||
last[v] = date.today() if v == 'created_date__date' else date.today() + timedelta(weeks=after)
|
||||
last[v] = last[v].replace(day = _calendar.monthrange(last[v].year, last[v].month)[1])
|
||||
|
||||
r = 8 * 30
|
||||
if v == 'start_day':
|
||||
r += after * 7
|
||||
first[v] = (last[v] - timedelta(days=r)).replace(day=1)
|
||||
|
||||
stats[v] = ev_published. \
|
||||
annotate(day=F(v)). \
|
||||
filter(Q(day__lte=last[v]) & Q(day__gte=first[v])).values('day').annotate(total=Count('day')).order_by('day')
|
||||
|
||||
|
||||
nb_by_city = ev_published.annotate(city=F('exact_location__city')).filter(city__isnull=False).values('city').annotate(total=Count('city')).order_by('-total')
|
||||
|
||||
|
||||
context = {"stats_by_startday": stats["start_day"], "stats_by_creation": stats["created_date__date"],
|
||||
"first_by_startday": first["start_day"], "last_by_startday": last["start_day"],
|
||||
"first_by_creation": first["created_date__date"], "last_by_creation": last["created_date__date"],
|
||||
"nb_by_city": nb_by_city
|
||||
}
|
||||
return render(request, "agenda_culturel/statistics.html", context)
|
||||
|
||||
|
||||
def tag_list(request):
|
||||
tags = Event.get_all_tags()
|
||||
r_tags = [t["tag"] for t in tags]
|
||||
|
Loading…
x
Reference in New Issue
Block a user