Amélioration de la généricité

On créé une classe singleton pour les variables de configuration

See #328
This commit is contained in:
Jean-Marie Favreau 2025-04-02 20:03:18 +02:00
parent d2966348fd
commit 15cb6d815a
10 changed files with 609 additions and 374 deletions

View File

@ -28,6 +28,8 @@ On peut aussi peupler les positions de référence qui serviront aux recherches
* ```make create-reference-locations```
Une fois l'installation réussie, la configuration du site est possible en allant modifier l'objet "Configuration du site" dans l'administration de django.
## Utilisation d'un proxy socket
On peut activer à la main (pour l'instant) un proxy type socket pour l'import d'événements.

View File

@ -4,6 +4,8 @@ from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
from django_better_admin_arrayfield.models.fields import DynamicArrayField
from solo.admin import SingletonModelAdmin
from .models import (
BatchImportation,
Category,
@ -17,8 +19,11 @@ from .models import (
StaticContent,
Tag,
UserProfile,
SiteConfiguration,
)
admin.site.register(SiteConfiguration, SingletonModelAdmin)
admin.site.register(Category)
admin.site.register(Tag)
admin.site.register(StaticContent)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
# Generated by Django 4.2.19 on 2025-04-02 19:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("agenda_culturel", "0160_alter_recurrentimport_processor"),
]
operations = [
migrations.CreateModel(
name="SiteConfiguration",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"site_name",
models.CharField(
default="Pommes de lune",
max_length=255,
verbose_name="Site name",
),
),
(
"site_description",
models.CharField(
default="Agenda participatif des sorties culturelles à Clermont-Ferrand et aux environs",
max_length=255,
verbose_name="Site description",
),
),
(
"html_keywords",
models.CharField(
default="Clermont-Ferrand, Puy-de-Dôme, agenda culturel, agenda participatif, sortir à clermont, sorties, concerts, théâtre, danse, animations, ateliers, lectures",
max_length=1024,
verbose_name="Keywords in html header",
),
),
(
"html_description",
models.CharField(
default="Où sortir à Clermont-Ferrand? Retrouve tous les bons plans sur l'agenda participatif des événements culturels à Clermont-Ferrand et dans le Puy-de-Dôme",
max_length=1024,
verbose_name="Description in html header",
),
),
(
"google_site_verification",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Google site verification value",
),
),
(
"ms_site_verification",
models.CharField(
blank=True,
default=None,
max_length=255,
null=True,
verbose_name="Microsoft (bing) site verification value",
),
),
(
"favicon",
models.ImageField(
blank=True,
max_length=1024,
null=True,
upload_to="",
verbose_name="Illustration",
),
),
(
"favicon_dev",
models.ImageField(
blank=True,
max_length=1024,
null=True,
upload_to="",
verbose_name="Illustration (development version)",
),
),
],
options={
"verbose_name": "Site Configuration",
},
),
]

View File

@ -46,7 +46,9 @@ from django.db.models.signals import post_save
from django.db.models.functions import Now
from django.db.models.functions import ExtractDay
from django.db.models.expressions import RawSQL
from django.templatetags.static import static
from django.conf import settings
from solo.models import SingletonModel
from .calendar import CalendarDay
from .import_tasks.extractor import Extractor
@ -62,6 +64,87 @@ logger = logging.getLogger(__name__)
to_be_translated = [_("mean"), _("median"), _("maximum"), _("minimum"), _("stdev")]
class SiteConfiguration(SingletonModel):
site_name = models.CharField(
verbose_name=_("Site name"), max_length=255, default="Pommes de lune"
)
site_description = models.CharField(
verbose_name=_("Site description"),
max_length=255,
default="Agenda participatif des sorties culturelles à Clermont-Ferrand et aux environs",
)
html_keywords = models.CharField(
verbose_name=_("Keywords in html header"),
max_length=1024,
default="Clermont-Ferrand, Puy-de-Dôme, agenda culturel, agenda participatif, sortir à clermont, sorties, concerts, théâtre, danse, animations, ateliers, lectures",
)
html_description = models.CharField(
verbose_name=_("Description in html header"),
max_length=1024,
default="Où sortir à Clermont-Ferrand? Retrouve tous les bons plans sur l'agenda participatif des événements culturels à Clermont-Ferrand et dans le Puy-de-Dôme",
)
google_site_verification = models.CharField(
verbose_name=_("Google site verification value"),
max_length=255,
default=None,
blank=True,
null=True,
)
ms_site_verification = models.CharField(
verbose_name=_("Microsoft (bing) site verification value"),
max_length=255,
default=None,
blank=True,
null=True,
)
favicon = models.ImageField(
verbose_name=_("Illustration"),
max_length=1024,
blank=True,
null=True,
)
favicon_dev = models.ImageField(
verbose_name=_("Illustration (development version)"),
max_length=1024,
blank=True,
null=True,
)
def guess_mimetype(url):
if url.endswith("png"):
return "image/png"
if url.endswith("jpg"):
return "image/jpg"
if url.endswith("svg"):
return "image/svg+xml"
if url.endswith("ico"):
return "image/x-icon"
return "image/jpg"
def get_favicon_url(self):
if settings.DEBUG:
if self.favicon_dev:
return self.favicon_dev.url
else:
return static("images/pdl-64-dev.png")
else:
if self.favicon:
return self.favicon.url
else:
return static("images/pdl-64.png")
def get_favicon_mimetype(self):
return SiteConfiguration.guess_mimetype(self.get_favicon_url())
def __str__(self):
return str(_("Site Configuration"))
class Meta:
verbose_name = _("Site Configuration")
class UserProfile(models.Model):
user = models.OneToOneField(
User,
@ -2453,7 +2536,10 @@ class Event(models.Model):
def export_to_ics(events, request):
cal = icalCal()
# Some properties are required to be compliant
cal.add("prodid", "-//Pommes de lune//pommesdelune.fr//")
cal.add(
"prodid",
"-//" + SiteConfiguration.get_solo().site_name + "//pommesdelune.fr//",
)
cal.add("version", "2.0")
for event in events:

View File

@ -66,8 +66,11 @@ INSTALLED_APPS = [
"template_profiler_panel",
"django_cleanup.apps.CleanupConfig",
"django_unused_media",
"solo.apps.SoloAppConfig",
]
SOLO_CACHE_TIMEOUT = 60 * 15 # 15 minutes
HONEYPOT_FIELD_NAME = "alias_name"
MIDDLEWARE = [

View File

@ -7,40 +7,39 @@
{% load duplicated_extra %}
{% load rimports_extra %}
{% load static %}
{% load solo_tags %}
{% get_solo 'agenda_culturel.SiteConfiguration' as site_config %}
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pommes de lune
<title>{{ site_config.site_name }}
{% block title %}{% endblock %}
</title>
{% if site_config.google_site_verification %}
<meta name="google-site-verification"
content="pvRD0rc_xIE-1IYmbao0kj5ngGo1IWxJqKwoxrQwxuA" />
<meta name="msvalidate.01" content="60E9E7DB667A3C4D6697261420DF3D1B" />
<meta name="keywords"
content="Clermont-Ferrand, Puy-de-Dôme, agenda culturel, agenda participatif, sortir à clermont, sorties, concerts, théâtre, danse, animations, ateliers, lectures">
content="{{ site_config.google_site_verification }}" />
{% endif %}
{% if site_config.ms_site_verification %}
<meta name="msvalidate.01" content="{{ site_config.ms_site_verification }}" />
{% endif %}
<meta name="keywords" content="{{ site_config.keywords }}">
<meta name="description"
content="{% block description %}Où sortir à Clermont-Ferrand? Retrouve tous les bons plans sur l'agenda participatif des événements culturels à Clermont-Ferrand et dans le Puy-de-Dôme{% endblock %}" />
content="{% block description %}{{ site_config.description }}{% endblock %}" />
<script src="{% static 'js/modal.js' %}"></script>
{% load static %}
<meta property="og:title"
content="Pommes de lune — {% block og_title %}{% endblock %}" />
content="{{ site_config.site_name }} — {% block og_title %}{% endblock %}" />
<meta property="og:description"
content="{% block og_description %}Où sortir à Clermont-Ferrand? Retrouve tous les bons plans sur l'agenda participatif des événements culturels à Clermont-Ferrand et dans le Puy-de-Dôme{% endblock %}" />
content="{% block og_description %}{{ site_config.description }}{% endblock %}" />
<meta property="og:image"
content="{% block og_image %}https://{{ request.get_host }}{% get_media_prefix %}screenshot.png{% endblock %}" />
<meta property="og:locale" content="fr_FR" />
<meta property="og:url" content="{{ request.build_absolute_uri }}" />
<link rel="stylesheet"
href="{% static 'air-datepicker/air-datepicker.css' %}">
{% if debug %}
<link rel="icon"
type="image/svg+xml"
href="{% static 'images/pdl-64-dev.png' %}">
{% else %}
<link rel="icon"
type="image/svg+xml"
href="{% static 'images/pdl-64.png' %}">
{% endif %}
type="{{ site_config.get_favicon_mimetype }}"
href="{{ site_config.get_favicon_url }}">
{% load compress %}
{% compress css %}
<link type="text/x-scss"
@ -108,15 +107,16 @@
<ul class="menu-droite">
<li>
<a href="{% url 'home' %}" aria-label="Retour accueil">
<img src="{% static 'images/pdl-64.png' %}" alt="logo pommes de lune" />
<img src="{{ site_config.get_favicon_url }}"
alt="logo {{ site_config.site_name }}" />
</a>
</li>
<li>
<div>
{% if user.is_authenticated %}{{ user.username }} @{% endif %}
<a href="{% url 'home' %}" aria-label="Retour accueil">Pommes de lune</a>
<a href="{% url 'home' %}" aria-label="Retour accueil">{{ site_config.site_name }}</a>
</div>
<div class="soustitre">Agenda participatif des sorties culturelles à Clermont-Ferrand et aux environs</div>
<div class="soustitre">{{ site_config.site_description }}</div>
</li>
</ul>
</nav>

View File

@ -4,6 +4,7 @@
{% block title %}
{% block og_title %}Statistiques{% endblock %}
{% endblock %}
{% load solo_tags %}
{% load cat_extra %}
{% block entete_header %}
{% css_categories %}
@ -15,11 +16,14 @@
<link rel="stylesheet" href="{% static 'cal-heatmap/cal-heatmap.css' %}">
{% endblock %}
{% block content %}
{% get_solo 'agenda_culturel.SiteConfiguration' as site_config %}
<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>
<p>
On retrouve sur cette page une synthèse des événements publiés sur l'agenda culturel {{ site_config.site_name }}.
</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">

View File

@ -101,6 +101,7 @@ from .models import (
Tag,
remove_accents,
UserProfile,
SiteConfiguration,
)
from .utils import PlaceGuesser
@ -1216,7 +1217,7 @@ def export_ical(request, cat=None, tag=None, organisation_pk=None, place_pk=None
extra += " - " + emoji.replace_emoji(tag, replace="")
response["Content-Disposition"] = "attachment; filename={0}{1}{2}".format(
"Pommes de lune", extra, ".ics"
SiteConfiguration.get_solo().site_name, extra, ".ics"
)
return response

View File

@ -50,3 +50,4 @@ requests==2.32.3
django-cleanup==9.0.0
django-unused-media==0.2.2
django-resized==1.0.3
django-solo==2.4.0