From 4cc8504f1f1a72f747a3947c1f5e0f3f15b8dd95 Mon Sep 17 00:00:00 2001
From: LE DURAND Matteo <matteo.le-durand@developpement-durable.gouv.fr>
Date: Thu, 27 Mar 2025 11:45:18 +0100
Subject: [PATCH] =?UTF-8?q?fix:=20r=C3=A9solution=20des=20filtres=20et=20d?=
 =?UTF-8?q?u=20clignotment?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 server.R | 278 +++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 209 insertions(+), 69 deletions(-)

diff --git a/server.R b/server.R
index 7e6b438..9a7b8cc 100644
--- a/server.R
+++ b/server.R
@@ -1,118 +1,258 @@
 server <- function(input, output, session) {
+  # Stocker les valeurs réactives
   r <- reactiveValues(
-    filteredData = all_data,
-    filteredByProject = all_data, # Étape intermédiaire filtrée par daterange et project_name
-    filteredetiquette = NULL,
-    filteredProjects = NULL,
-    filteredAuthors = NULL,
-    filteredReCodes = NULL,
-    filteredCategories = NULL,
-    filteredByDate = NULL,
+    filteredByDate = NULL,  # Filtrage uniquement par date
+    filteredData = NULL     # Filtrage final après application de tous les filtres
+
   )
 
-  # Filtrage par date et project_name uniquement
+  # 1️⃣ Filtrage prioritaire par date ----
   observeEvent(input$daterange, {
-    req(input$daterange)
+    req(input$daterange)  # Vérifier que la sélection est valide
+
+    # Filtrage initial sur la plage de dates
     r$filteredByDate <- all_data %>%
       filter(
         updated_at >= as.Date(input$daterange[[1]]) &
-          updated_at <= as.Date(input$daterange[[2]]) &
-          (if (length(input$project_name)) project_name %in% input$project_name else TRUE)
+          updated_at <= as.Date(input$daterange[[2]])
       )
-    updateFilters() # Mise à jour des choix basés sur le filtrage intermédiaire
+    # Mise à jour dynamique des autres filtres en fonction des données filtrées par date
+    updateFilters()
   })
 
-  # Mise à jour des autres filtres
+  # 2️⃣ Mise à jour dynamique des filtres ----
+  updateFilters <- reactive({
+    req(r$filteredByDate)  # Vérifier que les données sont disponibles
+    isolate({
+      updateSelectizeInput(session, "project_name",
+                           choices = unique(r$filteredByDate$project_name),
+                           selected = input$project_name)
+
+      updateSelectizeInput(session, "etiquette",
+                           choices = r$filteredByDate$etiquette %>%
+                             strsplit(split = ",") %>%
+                             unlist() %>%
+                             na.omit() %>%
+                             .[. != ""] %>%
+                             trimws() %>%
+                             unique(),
+                           selected = input$etiquette)
+
+      updateSelectizeInput(session, "auteur",
+                           choices = unique(r$filteredByDate$auteur),
+                           selected = input$auteur)
+
+      updateSelectizeInput(session, "categorie",
+                           choices = unique(r$filteredByDate$type),
+                           selected = input$categorie)
+
+      updateCheckboxGroupInput_dsfr( inputId = "re_code",
+                                     label = "Type d'évènement :",
+                                     choices = unique(r$filteredByDate$re_code),
+                                     selected = input$re_code,
+                                     inline = TRUE)
+    })
+  })
+  # 3️⃣ Filtrage final en fonction des autres filtres ----
   observe({
-    req(input$daterange)
+    req(r$filteredByDate)  # Vérifier que les données de base sont filtrées par date
+
     r$filteredData <- r$filteredByDate %>%
       filter(
-        (if (length(input$etiquette)) sapply(input$etiquette, function(t) grepl(t, etiquette)) %>% rowSums() > 0 else TRUE) &
+        (if (length(input$project_name)) project_name %in% input$project_name else TRUE) &
+          (if (length(input$etiquette)) sapply(input$etiquette, function(t) grepl(t, etiquette)) %>% rowSums() > 0 else TRUE) &
           (if (length(input$auteur)) auteur %in% input$auteur else TRUE) &
           (if (length(input$re_code)) re_code %in% input$re_code else TRUE) &
-          (if (length(input$categorie)) categorie %in% input$categorie else TRUE)
+          (if (length(input$categorie)) type %in% input$categorie else TRUE)
       )
-    updateFilters()
-  })
 
-  # Fonction de mise à jour des choix pour les filtres
-  updateFilters <- reactive({
-    # Basé sur les données filtrées par date et projet
-    updateSelectizeInput(session, "etiquette", choices = unique(r$filteredByDate$etiquette)%>%
-                           strsplit(split = ",") %>%  # Divise les chaînes en éléments séparés
-                           unlist() %>%               # Aplatit la liste obtenue
-                           na.omit() %>%              # Supprime les NA (au cas où)
-                           .[. != ""] %>%             # Supprime les chaînes vides
-                           trimws() %>%                # suppremie les espace avnt et apres
-                           unique() , selected = input$etiquette)
-    updateSelectizeInput(session, "author", choices = unique(r$filteredByDate$auteur), selected = input$auteur)
-    updateSelectizeInput(session, "categorie", choices = unique(r$filteredByDate$categorie), selected = input$categorie)
-
-    # Basé uniquement sur les données globales pour éviter que project_name soit affecté
-    updateSelectizeInput(session, "project_name", choices = sort(unique(r$filteredByDate$project_name)), selected = input$project_name)
+    # Mettre à jour les filtres restants
+    updateFilters()
   })
 
-  # Réinitialisation des filtres
+  # 4️⃣ Réinitialisation des filtres ----
   observeEvent(input$reset, {
     r$filteredByDate <- all_data %>%
-      filter(updated_at >= as.Date(input$daterange[[1]]) &
-               updated_at <= as.Date(input$daterange[[2]]))
-    r$filteredData <- r$filteredByDate
+      filter(
+        updated_at >= as.Date(input$daterange[[1]]) &
+          updated_at <= as.Date(input$daterange[[2]])
+      )
+    r$filteredData <- r$filteredByDate  # Reset total
+    updateSelectizeInput(session, "project_name", selected = NULL)
     updateSelectizeInput(session, "etiquette", selected = NULL)
-    updateSelectizeInput(session, "author", selected = NULL)
+    updateSelectizeInput(session, "auteur", selected = NULL)
     updateSelectizeInput(session, "categorie", selected = NULL)
+    updateCheckboxGroupInput_dsfr( "re_code", selected = NULL,inline = TRUE)
     updateFilters()
   })
-  output$lien <- renderUI({
-    projects_with_links <- r$filteredData %>%
-      filter(origine == "Gitlab_Forge" & project_name %in% input$project_name)
-
-    if (nrow(projects_with_links) == 0) {
-      return(NULL) # Aucun lien à afficher
-    }
-
-    urls <- paste0("https://gitlab-forge.din.developpement-durable.gouv.fr/dreal-pdl/csd/", projects_with_links$project_name)
-    links <- lapply(seq_along(unique(projects_with_links$project_name)), function(i) {
-      a(href = urls[i], target = "_blank", projects_with_links$project_name[i])
-    })
-    do.call(tagList, links)
-  })
 
-  # Graphique interactif
+  # 5️⃣ Graphique interactif ----
   output$filteredPlot2 <- plotly::renderPlotly({
-    req(r$filteredData)
+    req(r$filteredData)  # Vérifier qu'on a des données filtrées
     p <- ggplot2::ggplot(r$filteredData, ggplot2::aes(x = updated_at, y = as.factor(project_name))) +
       ggplot2::geom_point(ggplot2::aes(color = re_code, text = paste(updated_at, message, sep = "\n")),
                           size = 3, alpha = 0.5) +
       ggplot2::labs(shape = "Événement", color = "Événement", x = "") +
       ggplot2::scale_x_datetime(timezone = "Europe/Paris") +
-      gouvdown::theme_gouv()+
+      gouvdown::theme_gouv() +
       ggplot2::theme(
         axis.text.x = ggplot2::element_text(angle = 0, hjust = 1),
         axis.title.y = ggplot2::element_blank()
       )
 
-    plotly::ggplotly(p, tooltip = "text")
+    plotly::ggplotly(p, tooltip = "text", dynamicTicks = TRUE)    %>%
+      plotly::config(locale = "fr", displaylogo = FALSE)
   })
 
-
-  output$table <- renderDataTable({
+  # 6️⃣ Graphique des indicateurs ----
+  output$bar_chart <- renderPlotly({
     req(r$filteredData)
-    datatable(r$filteredData ,
-              options = list(
-                pageLength = 10,        # Nombre de lignes affichées par page
-                lengthMenu = c(5, 10, 25, 50),  # Choix du nombre de lignes
-                autoWidth = TRUE,        # Ajuste automatiquement la largeur des colonnes
-                scrollX = TRUE,          # Active le défilement horizontal
-                class = "display",       # Ajoute du style CSS
-                rownames = FALSE,         # Supprime les numéros de ligne,
-                searchHighlight = TRUE
-              )
+
+    percent_time <- as.numeric(max(r$filteredData$updated_at) - min(r$filteredData$updated_at)) /
+      as.numeric(max(all_data$updated_at) - min(all_data$updated_at)) * 100
+
+    # Création du dataset
+    data <- data.frame(
+      dataset = c("Commit", "Projet", "Heure"),
+      total = c(sum(r$filteredData$type == "commit"),
+                length(unique(r$filteredData$project_name)),
+                percent_time)
     )
+    plotly::ggplotly(
+      ggplot(data, aes(x = dataset, y = total, fill = dataset)) +
+        geom_bar(stat = "identity") +
+        coord_flip() +
+        scale_fill_manual(values = c("#3498db", "#e74c3c", "#2ecc71")) +
+        labs(
+          x = "Comparaison",
+          y = "Pourcentage / Nombre",
+          title = "Comparaison des Commits, Projets et Temps"
+        ) +
+        theme_minimal() +
+        theme(legend.position = "none") +
+        geom_text(
+          aes(label = paste0(round(total, 1),"%")),
+          hjust = -0.2
+        )
+    ) %>%
+      plotly::config(locale = "fr",
+                     displaylogo = FALSE)
   })
 
+  output$nb_projet <- renderText({
+    req(r$filteredData)
+    paste("Nombre de projets visualisés",length(unique(r$filteredData$project_name)),sep = " : ")
+  })
+  output$nb_commit <- renderText({
+    req(r$filteredData)
+    paste("Nombre de commits",sum(r$filteredData$type == "commit"),sep = " : ")
+  })
+  output$nb_temps <- renderText({
+    req(r$filteredData)
+    paste("Estimation du temps passé sur la période :",sep = " ",round(difftime(
+      max(r$filteredData$updated_at) , min(r$filteredData$updated_at),units = "hours")), " heures")
+  })
+
+
+  # 7️⃣ Table des résultats ----
+  output$table <- renderDT({
+    req(r$filteredData)
+    datatable(r$filteredData, options = list(
+      pageLength = 10,
+      lengthMenu = c(5, 10, 25, 50),
+      autoWidth = TRUE,
+      scrollX = TRUE,
+      class = "display",
+      rownames = FALSE,
+      searchHighlight = TRUE
+    ))
+  })
+
+  # 8️⃣ Graphique des indicateurs -----
+
+  output$min_value <- renderText({
+    req(input$project_name)  # Vérifie que l'input existe
+
+    # Nombre de projets sélectionnés
+    selected_projects <- length(unique(input$project_name))
+
+    # Condition : Si 1 seul projet est sélectionné
+    if (selected_projects == 1) {
+      # Filtrer les données pour ce projet spécifique
+      projet_data <- all_data %>%
+        filter(project_name == input$project_name)
+
+      min_value <- min(projet_data$updated_at, na.rm = TRUE)
+
+      paste("Date de création du projet : ", format(min_value, "%d %B %Y, %H:%M"))
+    } else {
+      "Sélectionnez un seul projet pour voir la date de création."
+    }
+  })
+
+
+  output$temps <- renderText({
+    req(input$project_name)  # Vérifie que l'input existe
+
+    # Nombre de projets sélectionnés
+    selected_projects <- length(unique(input$project_name))
+    # Condition : Si 1 seul projet est sélectionné
+    if (selected_projects == 1) {
+      # Filtrer les données pour ce projet spécifique
+      projet_data <- all_data %>%
+        filter(project_name == input$project_name)
+
+      temps <- difftime(max(projet_data$updated_at), min(projet_data$updated_at), units = "secs")
+
+      jours <- as.numeric(temps, units = "days") %/% 1
+      heures <- (as.numeric(temps, units = "hours") %% 24) %/% 1
+      minutes <- (as.numeric(temps, units = "mins") %% 60) %/% 1
+      print(temps)
+      paste("Estimation du temps passé : ", jours, "jours,", heures, "heures,", minutes, "minutes")
+    } else {
+      "Sélectionnez un seul projet pour voir l'estimation."
+    }
+  })
+
+  output$bar <- renderPlot({
+    req(input$project_name)  # Vérifie que l'input existe
+
+    selected_projects <- length(unique(input$project_name))
 
+    if (selected_projects == 1) {
+      # Filtrer les données pour le project_name sélectionné
+      filtered_data <- all_data %>%
+        filter(project_name == input$project_name) %>%
+        mutate(month = format(as.Date(updated_at), "%Y-%m"))
 
+      # Définir la plage de mois spécifique au projet sélectionné
+      first_month <- floor_date(min(as.Date(filtered_data$updated_at)), "month")
+      last_month <- ceiling_date(max(as.Date(filtered_data$updated_at)), "month")
 
+      # Générer tous les mois entre le premier et le dernier
+      full_months <- seq(first_month, last_month, by = "month") %>%
+        tibble(month = format(., "%Y-%m"))
 
+      # Compter les entrées par mois et compléter les mois manquants
+      complete_data <- full_months %>%
+        left_join(filtered_data %>% count(month), by = "month") %>%
+        replace_na(list(n = 0))
+
+      # Créer le bar plot
+      ggplot(complete_data, aes(x = month, y = n)) +
+        geom_bar(stat = "identity", fill = "#ff7f27", width = 0.7) +
+        theme_minimal() +
+        scale_x_discrete(
+          name = "Mois",
+          labels = function(x) format(as.Date(paste0(x, "-01")), "%B %Y")
+        ) +
+        labs(y = "Nombre d'entrées") +
+        gouvdown::theme_gouv() +
+        theme(axis.text.x = element_text(angle = 45, hjust = 1))
+    }else{
+      ggplot() +
+        annotate("text", x = 1, y = 1, label = "Sélectionnez un seul projet pour voir la timeline", size = 5) +
+        theme_void()
+    }
+  })
 }
-- 
GitLab