From 782c1cb49b4591ef945dcd73a4ae47f55a41e24f Mon Sep 17 00:00:00 2001 From: "Alexis A." Date: Thu, 18 Jul 2024 10:15:01 +0200 Subject: [PATCH] feat(rnu-packages): misc --- home/templates/home/download.html | 43 ++++++--- home/templates/home/home.html | 2 +- home/templates/home/home_rapport_local.html | 2 +- home/views.py | 9 +- project/models/RNUPackage.py | 15 +++ project/tasks/project.py | 12 ++- .../templates/project/rnu_package_notice.html | 83 +++++++++++++++++ project/urls.py | 1 - project/views/RNUPackagesProgressView.py | 92 ------------------- project/views/__init__.py | 1 - .../commands/create_rnu_diagnostics.py | 6 +- .../commands/create_rnu_packages.py | 42 +++++---- users/templates/users/signin.html | 24 ++++- 13 files changed, 196 insertions(+), 136 deletions(-) create mode 100644 project/templates/project/rnu_package_notice.html delete mode 100644 project/views/RNUPackagesProgressView.py diff --git a/home/templates/home/download.html b/home/templates/home/download.html index 6d0bc36a9..349243d60 100644 --- a/home/templates/home/download.html +++ b/home/templates/home/download.html @@ -18,17 +18,38 @@ {% block content %}
-

Téléchargements

-

Trames de rapport triennal local des communes sous RNU

-

Mon Diagnostic Artificialisation met à disposition des DDT les trames de rapport triennal local par paquets de leurs communes sous RNU

-
    - {% for departement in departements %} -
  • -
    {{ departement.source_id }} {{ departement.name }}
    - -
  • - {% endfor %} -
+

Trames de rapport triennal local des communes au RNU

+

+ Mon Diagnostic Artificialisation met à disposition des DDT les trames de rapport triennal local par paquets de leurs communes au RNU +

+
+
+
+
+ + + + + + + + + + + {% for package in rnu_packages %} + + + + + + + {% endfor %} + +
DépartementNombre de communes au RNUDate de créationLien
{{ package.departement_official_id }} - {{ package.departement.name }}{{ package.communes|length }}{{ package.created_at }}Lien de téléchargement
+
+
+
+
{% include "home/partials/newsletter_form.html" %} diff --git a/home/templates/home/home.html b/home/templates/home/home.html index c02fbe924..bac8712c7 100644 --- a/home/templates/home/home.html +++ b/home/templates/home/home.html @@ -30,7 +30,7 @@

Mon Diagnostic Artificialisation vous aide à analyser et ma

Nouveau

Exportez une trame de votre rapport triennal local de suivi de l'artificialisation des sols conformément à l'article L. 2231-1 du code général des collectivités territoriales.
- Pour les DDT, ces trames sont disponibles en téléchargement par paquets pour les communes sous RNU. + Pour les DDT, ces trames sont disponibles en téléchargement par paquets pour les communes au RNU.

diff --git a/home/templates/home/home_rapport_local.html b/home/templates/home/home_rapport_local.html index 88d46ee93..4eb67767e 100644 --- a/home/templates/home/home_rapport_local.html +++ b/home/templates/home/home_rapport_local.html @@ -29,7 +29,7 @@

Préparer le rapport triennal local de suivi de l’artifici

- Pour les DDT, ces trames sont disponibles en téléchargement par paquets pour les communes sous RNU. + Pour les DDT, ces trames sont disponibles en téléchargement par paquets pour les communes au RNU.

diff --git a/home/views.py b/home/views.py index 4e68e1602..624bc48e6 100644 --- a/home/views.py +++ b/home/views.py @@ -3,7 +3,7 @@ from django.conf import settings from django.contrib import messages -from django.contrib.auth.mixins import UserPassesTestMixin +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.db.models import F, Value from django.http import HttpRequest, HttpResponse, HttpResponseGone from django.shortcuts import redirect @@ -12,8 +12,7 @@ from django_app_parameter import app_parameter from brevo.connectors import Brevo -from project.models import Request -from public_data.models import Departement +from project.models import Request, RNUPackage from users.models import User from utils.functions import get_url_with_domain from utils.htmx import HtmxRedirectMixin, StandAloneMixin @@ -36,13 +35,13 @@ def get_context_data(self, **kwargs): return super().get_context_data(**kwargs) -class DownloadView(BreadCrumbMixin, TemplateView): +class DownloadView(LoginRequiredMixin, BreadCrumbMixin, TemplateView): template_name = "home/download.html" def get_context_data(self, **kwargs): kwargs |= { "form": NewsletterForm(), - "departements": Departement.objects.all().order_by("source_id"), + "rnu_packages": RNUPackage.objects.all().order_by("departement_official_id"), } return super().get_context_data(**kwargs) diff --git a/project/models/RNUPackage.py b/project/models/RNUPackage.py index 545e87e09..b86b969eb 100644 --- a/project/models/RNUPackage.py +++ b/project/models/RNUPackage.py @@ -1,4 +1,8 @@ from django.db import models +from django.utils.functional import cached_property + +from public_data.models import Departement +from public_data.models.sudocuh import DocumentUrbanismeChoices, Sudocuh class RNUPackage(models.Model): @@ -10,3 +14,14 @@ class RNUPackage(models.Model): max_length=10, unique=True, ) + + @cached_property + def departement(self): + return Departement.objects.get(source_id=self.departement_official_id) + + @cached_property + def communes(self): + sudocuh = Sudocuh.objects.filter(du_opposable=DocumentUrbanismeChoices.RNU) + return self.departement.commune_set.filter( + insee__in=sudocuh.values("code_insee"), + ) diff --git a/project/tasks/project.py b/project/tasks/project.py index 0cede44bc..f52e6462c 100644 --- a/project/tasks/project.py +++ b/project/tasks/project.py @@ -29,6 +29,7 @@ from public_data.domain.containers import PublicDataContainer from public_data.models import AdminRef, ArtificialArea, Departement, Land, OcsgeDiff from public_data.models.gpu import ArtifAreaZoneUrba, ZoneUrba +from public_data.storages import DataStorage from utils.db import fix_poly from utils.emails import SibTemplateEmail from utils.functions import get_url_with_domain @@ -890,9 +891,18 @@ def create_zip_departement_rnu_package_one_off(departement_id: str) -> None: project__land_id__in=commune_in_departement_ids_as_string, ) + notice_file_path = f"rnu_packages/NOTICE_{departement_id}.pdf" + rnu_communes_map_file_path = f"rnu_packages/COMM_DU_{departement_id}.pdf" + file_name = f"rnu_package_departement_{departement_id}.zip" - with zipfile.ZipFile(file_name, "a", compression=zipfile.ZIP_DEFLATED) as zipf: + with zipfile.ZipFile(file_name, "a", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zipf: + notice_file = DataStorage().open(notice_file_path, "rb") + rnu_communes_map_file = DataStorage().open(rnu_communes_map_file_path, "rb") + + zipf.writestr(f"NOTICE_{departement_id}.pdf", notice_file.read()) + zipf.writestr(f"COMM_DU_{departement_id}.pdf", rnu_communes_map_file.read()) + for request in requests_created_by_the_rnu_package_service_account: if not request.sent_file: raise ValueError(f"Request {request.id} has no sent file") diff --git a/project/templates/project/rnu_package_notice.html b/project/templates/project/rnu_package_notice.html new file mode 100644 index 000000000..3b6dc3108 --- /dev/null +++ b/project/templates/project/rnu_package_notice.html @@ -0,0 +1,83 @@ +{% load sri %} +{% sri_static "assets/styles/main.css" %} + + + + + + + +

Paquet de rapports locaux des communes au RNU du département {{ object.departement.name }} ({{ object.departement.source_id }})

+ +

+ Ce paquet de trames de rapport local s'adresse aux DDT. + Il contient un rapport local par commune au RNU du département {{ object.departement.name }} ({{ object.departement.source_id }}). +

+

+ Une version web de ces diagnostics est disponible. + Un lien est disponible dans le bas de page de chaque rapport. +

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + {% for commune in communes %} + + + + + {% endfor %} + +
+ Liste des fichiers du paquet +
FichierChemin
Ce documentNOTICE_{{ object.departement_official_id }}.pdf
Carte des communes au RNU du départementCOMM_DU_{{ object.departement_official_id }}.pdf
Rapport de + {{ commune.name }} + {{ commune.departement.source_id }}_COMM_{{ commune.insee }}.docx
+
+
+
+
+
+

+ Crée par Mon Diagnostic Artificialisation +

+ + + \ No newline at end of file diff --git a/project/urls.py b/project/urls.py index d99ff5456..1bb977da9 100644 --- a/project/urls.py +++ b/project/urls.py @@ -195,7 +195,6 @@ path("/export-excel", views.ExportExcelView.as_view(), name="export-excel"), # SUB APPS path("test", views.TestView.as_view(), name="test"), - path(route="rnu-packages-progress", view=views.RNUPackagesProgressView.as_view(), name="rnu-packages-progress"), ] diff --git a/project/views/RNUPackagesProgressView.py b/project/views/RNUPackagesProgressView.py deleted file mode 100644 index dca525834..000000000 --- a/project/views/RNUPackagesProgressView.py +++ /dev/null @@ -1,92 +0,0 @@ -from django.db.models import F, IntegerField, Sum -from django.db.models.functions import Cast -from django.utils import timezone -from rest_framework.response import Response -from rest_framework.views import APIView - -from project.models import Project -from public_data.models import Commune, Departement -from public_data.models.sudocuh import DocumentUrbanismeChoices, Sudocuh -from users.models import User - - -class RNUPackagesProgressView(APIView): - def get(self, request): - diagnostic_to_create = Sudocuh.objects.filter(du_opposable=DocumentUrbanismeChoices.RNU) - from_commune_table = Commune.objects.filter( - insee__in=diagnostic_to_create.values("code_insee"), - ) - of_those_with_ocsge = from_commune_table.filter( - ocsge_available=True, - ) - of_those_with_ocsge_count = of_those_with_ocsge.count() - diagnostic_to_create_count = diagnostic_to_create.count() - mda_user = User.objects.get(email="rnu.package@mondiagartif.beta.gouv.fr") - - diagnostic_created = Project.objects.filter( - user=mda_user, - ) - diagnostic_created_count = diagnostic_created.count() - - async_fields = [ - "async_add_city_done", - "async_set_combined_emprise_done", - "async_cover_image_done", - "async_find_first_and_last_ocsge_done", - "async_add_comparison_lands_done", - "async_ocsge_coverage_status_done", - ] - - async_fields_with_ocsge = [ - "async_theme_map_understand_artif_done", - ] - - aggregate_results = [] - - for field in async_fields: - aggregate_results.append( - diagnostic_created.aggregate( - **{ - f"{field}_count": Sum(Cast(field, IntegerField())), - f"{field}_percentage": F(f"{field}_count") * 100.0 / diagnostic_to_create_count, - } - ) - ) - for field in async_fields_with_ocsge: - aggregate_results.append( - diagnostic_created.aggregate( - **{ - f"{field}_count": Sum(Cast(field, IntegerField())), - f"{field}_percentage": F(f"{field}_count") * 100.0 / of_those_with_ocsge_count, - } - ) - ) - - time_diff = timezone.now() - mda_user.date_joined - hours = time_diff.seconds // 3600 - minutes = (time_diff.seconds % 3600) // 60 - seconds = time_diff.seconds % 60 - - response_data = { - "elapsed_time": f"{hours}h {minutes}m {seconds}s", - "diagnostic_to_create_count": diagnostic_to_create_count, - "of_those_with_ocsge_count": of_those_with_ocsge_count, - "diagnostic_created_count": diagnostic_created_count, - "diangostic_created_percentage": f"{diagnostic_created_count / diagnostic_to_create_count * 100}%", - "async_operations_progress": aggregate_results, - } - - for departement in Departement.objects.all(): - response_data[f"department_{departement.source_id}"] = { - "diagnostic_to_create_count": diagnostic_to_create.filter( - code_insee__startswith=departement.source_id - ).count(), - "of_those_with_ocsge_count": of_those_with_ocsge.filter(departement=departement).count(), - "diagnostic_created_count": diagnostic_created.annotate( - land_id_as_int=Cast("land_id", output_field=IntegerField()) - ) - .filter(land_id_as_int__in=from_commune_table.filter(departement=departement).values("id")) - .count(), - } - - return Response(response_data) diff --git a/project/views/__init__.py b/project/views/__init__.py index 4c4c70168..69faad1a0 100644 --- a/project/views/__init__.py +++ b/project/views/__init__.py @@ -4,4 +4,3 @@ from .export import * from .map import * from .report import * -from .RNUPackagesProgressView import RNUPackagesProgressView diff --git a/public_data/management/commands/create_rnu_diagnostics.py b/public_data/management/commands/create_rnu_diagnostics.py index 950e032fd..78eafbaff 100644 --- a/public_data/management/commands/create_rnu_diagnostics.py +++ b/public_data/management/commands/create_rnu_diagnostics.py @@ -17,7 +17,7 @@ class Command(BaseCommand): help = "create_rnu_diagnostics" def add_arguments(self, parser): - parser.add_argument("--departement", type=str, required=True) + parser.add_argument("--departement", type=str) def handle(self, *args, **options): mondiagartif_user, _ = User.objects.get_or_create( @@ -32,10 +32,12 @@ def handle(self, *args, **options): projects = [] communes = Commune.objects.filter( - departement__source_id=options["departement"], insee__in=[Sudocuh.objects.filter(du_opposable=DocumentUrbanismeChoices.RNU).values("code_insee")], ) + if options["departement"]: + communes = communes.filter(departement__source_id=options["departement"]) + logger.info(f"Found {len(communes)} RNU communes") projects_to_delete = Project.objects.filter( diff --git a/public_data/management/commands/create_rnu_packages.py b/public_data/management/commands/create_rnu_packages.py index 42b3d45d2..133ca3049 100644 --- a/public_data/management/commands/create_rnu_packages.py +++ b/public_data/management/commands/create_rnu_packages.py @@ -7,6 +7,7 @@ from project.tasks import create_zip_departement_rnu_package_one_off from public_data.models import Commune, Departement, Sudocuh from public_data.models.sudocuh import DocumentUrbanismeChoices +from users.models import User logger = logging.getLogger("management.commands") @@ -17,29 +18,21 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument("--departement", type=str, required=False) - def handle(self, *args, **options): - tasks = [] - - departements = Departement.objects.all() - - if options["departement"]: - departements = Departement.objects.filter(source_id=options["departement"]) - - communes = Commune.objects.all() - - if options["departement"]: - communes = Commune.objects.filter( - departement__source_id=options["departement"], - insee__in=[Sudocuh.objects.filter(du_opposable=DocumentUrbanismeChoices.RNU).values("code_insee")], - ) + def check_requests_are_created_for_departement(self, departement): + communes = Commune.objects.filter( + insee__in=[Sudocuh.objects.filter(du_opposable=DocumentUrbanismeChoices.RNU).values("code_insee")], + departement=departement, + ) commune_count = communes.count() request_for_communes_count = Request.objects.filter( - done=True, project__land_id__in=[str(commune.id) for commune in communes] + done=True, + project__land_id__in=[str(commune.id) for commune in communes], + user=User.objects.get(email="rnu.package@mondiagartif.beta.gouv.fr"), ).count() if commune_count != request_for_communes_count: - raise ValueError( + logger.error( ( f"Commune count ({commune_count}) does not match " f"request for communes count ({request_for_communes_count})" @@ -50,8 +43,19 @@ def handle(self, *args, **options): f"Commune count ({commune_count}) matches request for communes count ({request_for_communes_count})" ) + return True + + def handle(self, *args, **options): + tasks = [] + + departements = Departement.objects.all() + + if options["departement"]: + departements = departements.filter(source_id=options["departement"]) + for departement in departements: - logger.info(f"Creating RNU package for departement {departement.source_id}") - tasks.append(create_zip_departement_rnu_package_one_off.si(departement.source_id)) + if self.check_requests_are_created_for_departement(departement): + logger.info(f"Creating RNU package for departement {departement.source_id}") + tasks.append(create_zip_departement_rnu_package_one_off.si(departement.source_id)) celery.group(*tasks).apply_async(queue="long") diff --git a/users/templates/users/signin.html b/users/templates/users/signin.html index a584df398..28c6dda0e 100644 --- a/users/templates/users/signin.html +++ b/users/templates/users/signin.html @@ -8,8 +8,28 @@
- +

Connexion

+ {% if next == '/telechargements' %} +
+
+
+
+

+ La page de téléchargement de paquet de rapports locaux est uniquement accessible aux utilisateurs enregistrés et connectés. +

+
+

+ + Vous pouvez vous connecter ci-dessous, ou créer un compte. + +

+
+
+
+
+ {% endif %} +
{% csrf_token %} @@ -19,7 +39,7 @@

Connexion

Mot de pass oublié ? Réinitialisez le. -
Pas encore de compte ? Inscrivez-vous. +
Pas encore de compte ? Inscrivez-vous.