diff --git a/.gitignore b/.gitignore index 953854d370ec1a9108c586aab1a4b6fef79e24b9..4f6e7b70307af5f3d50c068adb7d602e534e3586 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .Rproj.user .Rhistory -.RData +*.RData .Ruserdata -datamart_gitlabr.RData + diff --git a/datamart_gitlabr.RData b/datamart_gitlabr.RData index 6eb8b39aa6b618f26a05522dc27ffa5876efb333..dbb96fc7034db2365734a8755a6d316f69028db6 100644 Binary files a/datamart_gitlabr.RData and b/datamart_gitlabr.RData differ diff --git a/dev/script_chargement_rdata.R b/dev/script_chargement_rdata.R new file mode 100644 index 0000000000000000000000000000000000000000..573ce30febf3f91402e8d7bf589c6c2ae00fed80 --- /dev/null +++ b/dev/script_chargement_rdata.R @@ -0,0 +1,144 @@ +library(gitlabr) +library(dplyr) +library(purrr) +library(httr) +library(jsonlite) +library(utils) + +gitlab_url <- "https://gitlab-forge.din.developpement-durable.gouv.fr" +token <- Sys.getenv("GITLAB_COM_TOKEN") + +gitlabr::set_gitlab_connection( + gitlab_url = gitlab_url, # adresse internet + private_token = token +) # Token personelle pour connexion + +# attention j'ai du modifier le filtre puisque les variable ne correspondait plus a ce de la veille on va peut etre partire sur le select() +group <- gitlabr::gl_list_group_projects(group_id = 1013, max_page = 10) +group <- group %>% dplyr::select(id, name, created_at, star_count, last_activity_at, open_issues_count, updated_at, description, contains("members"), path) # _links.members +# premier filtre pour les groupes +groupf <- (group[as.numeric(group$open_issues_count) != 0, ]) +groupf <- groupf %>% + mutate( + name_encoded = utils::URLencode(name), # Encode les espaces et autres caractères spéciaux + analyse = paste0( + "<a href='https://gitlab-forge.din.developpement-durable.gouv.fr/dreal-pdl/csd/", + name_encoded, + "/-/value_stream_analytics' target='_blank'>GitLab_analytics</a>" + ) + ) %>% + dplyr::select(id, name, created_at, star_count, last_activity_at, open_issues_count, updated_at, description, analyse, contains("members")) + +groupf$open_issues_count <- as.numeric(groupf$open_issues_count) +# choix par identifiant de projet +choice <- groupf$id %>% stats::setNames(groupf$name) + +# creation du df pour le journal d'activité ---- + +final_table0 <- purrr::map_dfr(groupf$id, gitlabr::gl_list_issues) + +final_table <- final_table0 %>% + dplyr::mutate(id = project_id) %>% + dplyr::select(id, project_id, title, description, state, created_at, updated_at, labels, labels1, labels2, labels3, assignees.username, assignees.state, author.username, type, confidential, references.short, closed_at, closed_by.username) %>% + dplyr::left_join(dplyr::select(groupf, id, name), by = "id") %>% + dplyr::select(name, project_id, title, description, state, created_at, updated_at, labels, labels1, labels2, labels3, assignees.username, assignees.state, author.username, type, confidential, references.short, closed_at, closed_by.username) + +# fonction API ---- +# Fonction pour récupérer les événements GitLab pour un projet +get_gitlab_events <- function(project_id, event_type = NULL) { + url <- paste0(gitlab_url, "/api/v4/projects/", project_id, "/events") + headers <- add_headers(`PRIVATE-TOKEN` = token) + + # Construire la requête en fonction du type d'événement souhaité + params <- list(per_page = 1500) # Limite à 1500 événements par requête + if (!is.null(event_type)) { + params[["action"]] <- event_type + } + + response <- GET(url, headers, query = params) + httr::stop_for_status(response) + + if (status_code(response) != 200) { + stop("Échec de la récupération des données pour le projet ", project_id, " : ", content(response, "text")) + } + + events <- fromJSON(content(response, "text"), flatten = TRUE) + events$project_id <- project_id # Ajouter l'identifiant du projet pour le suivi + return(events) +} + +# Fonction pour récupérer les événements pour plusieurs projets +get_events_for_projects <- function(project_ids, event_type = NULL) { + all_events <- map(project_ids, ~ { + cat("Récupération des événements pour le projet : ", .x, "\n") + tryCatch( + { + get_gitlab_events(.x, event_type) + }, + error = function(e) { + message("Erreur lors de la récupération des événements pour le projet ", .x, " : ", e$message) + return(NULL) # Retourner NULL en cas d'erreur pour ce projet + } + ) + }) %>% + set_names(project_ids) %>% + discard(is.null) + + all_events_df <- bind_rows(all_events, .id = "project_id") + return(all_events_df) +} + +# Liste des identifiants de projets +project_ids <- as.list(groupf$id) # Remplacez par vos identifiants de projets + +# Récupérer tous les événements pour tous les projets +all_events <- get_events_for_projects(project_ids) +# Convertir les données en un DataFrame +all_events_df <- as_tibble(all_events) + +## pour le graphique temps/projet +# traduction des type +traduction <- c( + "created" = "Création", + "DiffNote" = "Note d'information", + "DiscussionNote" = "Note de discussion", + "Issue" = "Ticket", + "joined" = "Rejoint", + "MergeRequest" = "Demande de fusion", + "pushed to" = "poussé vers", + "WikiPage::Meta" = "Page Wiki" +) + +data_chronol <- all_events_df %>% + dplyr::mutate( + id = project_id, + type = dplyr::coalesce(target_type, action_name), + type = as.factor(type), + temps = lubridate::as_datetime(created_at), + info = dplyr::coalesce(push_data.commit_title, target_title) + ) %>% + dplyr::select(id, type, temps, info) %>% + dplyr::left_join(dplyr::select(groupf, id, name), by = "id") +# au cas ou il reste des valeur manquante +data_chronol$info[is.na(data_chronol$info)] <- "" +data_chronol <- dplyr::distinct(data_chronol) +data_chronol <- data_chronol %>% dplyr::filter(!type %in% c("deleted", "pushed new")) + +# Traduire les niveaux de facteur +data_chronol <- data_chronol %>% + dplyr::mutate(type = dplyr::recode(type, !!!traduction)) + +save.image("datamart_gitlabr.RData") + +# deploiement vers le sserveur interne de dataviz---------------------------------------- +library(RCurl) +# creation de l'adresse de connexion FTP au serveur +con_ftp_svr_dataviz = paste0("ftp://", Sys.getenv("svr_dataviz_user_dtv"), ":", Sys.getenv("svr_dataviz_mdp_dtv"), + "@", Sys.getenv("svr_dataviz_ip"), "/gitlabr/") + +# creation d'une fonction de transfert FTP vers le serveur à l'aide de ftpUpload de RCurl +## Les adresses des fichiers à transferer parte de la racine du projet RStudio +to_svr_dataviz_ftp <- function(fic = "app/shiny_siclop.RData") { + ftpUpload(what = fic, to = paste0(con_ftp_svr_dataviz, fic))} + +to_svr_dataviz_ftp("datamart_gitlabr.RData") diff --git a/dev/script_routine_dataviz_gitlabr.R b/dev/script_routine_dataviz_gitlabr.R new file mode 100644 index 0000000000000000000000000000000000000000..8cb3a213b184ee66d7bd9400ca1bdff85b6c252a --- /dev/null +++ b/dev/script_routine_dataviz_gitlabr.R @@ -0,0 +1,6 @@ +Sys.setenv("HTTP_PROXY" = "http://pfrie-std.proxy.e2.rie.gouv.fr:8080") +Sys.setenv("HTTPS_PROXY" = "http://pfrie-std.proxy.e2.rie.gouv.fr:8080") +setwd("T:\\datalab\\SCTE_CSD\\dataviz_gitlabr\\") + +# etape de mise à jour des données sur T------------- +source(file = "dev/script_chargement_rdata.R", verbose = TRUE, echo = TRUE) diff --git a/global.R b/global.R index d7d7197c963f41e766b5faa59863158638592d1b..039325401454dc708c0ab7546559b7c540d2b3a9 100644 --- a/global.R +++ b/global.R @@ -16,126 +16,9 @@ library(httr) library(jsonlite) library(utils) -# inisialisation de connexion pour GitlabR -gitlab_url <- "https://gitlab-forge.din.developpement-durable.gouv.fr" -token <- Sys.getenv("GITLAB_COM_TOKEN") +# chargement des données (résultat du script "dev/script_chargement_rdata.R") +load("datamart_gitlabr.RData") -gitlabr::set_gitlab_connection( - gitlab_url = gitlab_url, # adresse internet - private_token = token -) # Token personelle pour connexion -# attention j'ai du modifier le filtre puisque les variable ne correspondait plus a ce de la veille on va peut etre partire sur le select() -group <- gitlabr::gl_list_group_projects(group_id = 1013, max_page = 10) -group <- group %>% dplyr::select(id, name, created_at, star_count, last_activity_at, open_issues_count, updated_at, description, contains("members"), path) # _links.members -# premier filtre pour les groupes -groupf <- (group[as.numeric(group$open_issues_count) != 0, ]) -groupf <- groupf %>% - mutate( - name_encoded = utils::URLencode(name), # Encode les espaces et autres caractères spéciaux - analyse = paste0( - "<a href='https://gitlab-forge.din.developpement-durable.gouv.fr/dreal-pdl/csd/", - name_encoded, - "/-/value_stream_analytics' target='_blank'>GitLab_analytics</a>" - ) - ) %>% - dplyr::select(id, name, created_at, star_count, last_activity_at, open_issues_count, updated_at, description, analyse, contains("members")) -groupf$open_issues_count <- as.numeric(groupf$open_issues_count) -# choix par identifiant de projet -choice <- groupf$id %>% stats::setNames(groupf$name) -# creation du df pour le journal d'activité ---- - -final_table0 <- purrr::map_dfr(groupf$id, gitlabr::gl_list_issues) - -final_table <- final_table0 %>% - dplyr::mutate(id = project_id) %>% - dplyr::select(id, project_id, title, description, state, created_at, updated_at, labels, labels1, labels2, labels3, assignees.username, assignees.state, author.username, type, confidential, references.short, closed_at, closed_by.username) %>% - dplyr::left_join(dplyr::select(groupf, id, name), by = "id") %>% - dplyr::select(name, project_id, title, description, state, created_at, updated_at, labels, labels1, labels2, labels3, assignees.username, assignees.state, author.username, type, confidential, references.short, closed_at, closed_by.username) - -# fonction API ---- -# Fonction pour récupérer les événements GitLab pour un projet -get_gitlab_events <- function(project_id, event_type = NULL) { - url <- paste0(gitlab_url, "/api/v4/projects/", project_id, "/events") - headers <- add_headers(`PRIVATE-TOKEN` = token) - - # Construire la requête en fonction du type d'événement souhaité - params <- list(per_page = 1500) # Limite à 1500 événements par requête - if (!is.null(event_type)) { - params[["action"]] <- event_type - } - - response <- GET(url, headers, query = params) - httr::stop_for_status(response) - - if (status_code(response) != 200) { - stop("Échec de la récupération des données pour le projet ", project_id, " : ", content(response, "text")) - } - - events <- fromJSON(content(response, "text"), flatten = TRUE) - events$project_id <- project_id # Ajouter l'identifiant du projet pour le suivi - return(events) -} - -# Fonction pour récupérer les événements pour plusieurs projets -get_events_for_projects <- function(project_ids, event_type = NULL) { - all_events <- map(project_ids, ~ { - cat("Récupération des événements pour le projet : ", .x, "\n") - tryCatch( - { - get_gitlab_events(.x, event_type) - }, - error = function(e) { - message("Erreur lors de la récupération des événements pour le projet ", .x, " : ", e$message) - return(NULL) # Retourner NULL en cas d'erreur pour ce projet - } - ) - }) %>% - set_names(project_ids) %>% - discard(is.null) - - all_events_df <- bind_rows(all_events, .id = "project_id") - return(all_events_df) -} - -# Liste des identifiants de projets -project_ids <- as.list(groupf$id) # Remplacez par vos identifiants de projets - -# Récupérer tous les événements pour tous les projets -all_events <- get_events_for_projects(project_ids) -# Convertir les données en un DataFrame -all_events_df <- as_tibble(all_events) - -## pour le graphique temps/projet -# traduction des type -traduction <- c( - "created" = "Création", - "DiffNote" = "Note d'information", - "DiscussionNote" = "Note de discussion", - "Issue" = "Ticket", - "joined" = "Rejoint", - "MergeRequest" = "Demande de fusion", - "pushed to" = "poussé vers", - "WikiPage::Meta" = "Page Wiki" -) - -data_chronol <- all_events_df %>% - dplyr::mutate( - id = project_id, - type = dplyr::coalesce(target_type, action_name), - type = as.factor(type), - temps = lubridate::as_datetime(created_at), - info = dplyr::coalesce(push_data.commit_title, target_title) - ) %>% - dplyr::select(id, type, temps, info) %>% - dplyr::left_join(dplyr::select(groupf, id, name), by = "id") -# au cas ou il reste des valeur manquante -data_chronol$info[is.na(data_chronol$info)] <- "" -data_chronol <- dplyr::distinct(data_chronol) -data_chronol <- data_chronol %>% dplyr::filter(!type %in% c("deleted", "pushed new")) - -# Traduire les niveaux de facteur -data_chronol <- data_chronol %>% - dplyr::mutate(type = dplyr::recode(type, !!!traduction)) diff --git a/server.R b/server.R index c332b0ba21828eb90694c76f2bf5dc7ac0afef74..9a79618f643f3de8897cae9933fb4b7ad3ad6c58 100644 --- a/server.R +++ b/server.R @@ -1,52 +1,68 @@ server <- function(input, output) { output$tablef <- - DT::renderDT({ DT::datatable(groupf, class = "display",options = list( - pageLength = 6 , - order = list(6 , "desc"), - scrollY = 300, scrollX = 400, scroller = TRUE - ), escape = FALSE - ) - } - ) + DT::renderDT({ + DT::datatable(groupf, class = "display", options = list( + pageLength = 10, + order = list(6, "desc"), + scrollY = 1000, scrollX = 400, scroller = TRUE + ), escape = FALSE) + }) output$issue_graph <- plotly::renderPlotly({ p <- ggplot2::ggplot(groupf) + - ggplot2::aes(x = reorder(name,-open_issues_count), y = open_issues_count) + + ggplot2::aes(x = reorder(name, -open_issues_count), y = open_issues_count) + ggplot2::geom_col(fill = "#000091") + gouvdown::theme_gouv() + - ggplot2::theme(legend.position = "none", - axis.title.x = element_blank(), - axis.text.x = element_blank(), - axis.ticks.x = element_blank(), - axis.title.y = element_blank()) + ggplot2::theme( + legend.position = "none", + axis.title.x = element_blank(), + axis.text.x = element_blank(), + axis.ticks.x = element_blank(), + axis.title.y = element_blank() + ) plotly::ggplotly(p) }) # filtre les issues pour seulement garder ls variable interresante et celles encore ouverte pourl'exploration des données - groupid <- shiny::reactive({gitlabr::gl_list_issues(project = input$ID1) %>% - dplyr::select(iid , - title, - description , - state , - created_at , - updated_at , - type, - labels, - user_notes_count) }) - groupidf <- shiny::reactive({groupid() %>% dplyr::filter( state == "opened")}) + groupid <- shiny::reactive({ + gitlabr::gl_list_issues(project = input$ID1) %>% + dplyr::select( + iid, + title, + description, + state, + created_at, + updated_at, + type, + labels, + user_notes_count + ) + }) + groupidf <- shiny::reactive({ + groupid() %>% dplyr::filter(state == "opened") + }) - nfermer <- shiny::reactive({nrow(groupid() %>% dplyr::filter( state == "closed"))}) - output$issue_nbr <- shiny::renderText({paste(nfermer(),"issues ont été fermées sur le projet", input$ID1)}) + nfermer <- shiny::reactive({ + nrow(groupid() %>% dplyr::filter(state == "closed")) + }) + output$issue_nbr <- shiny::renderText({ + paste(nfermer(), "issues ont été fermées sur le projet", input$ID1) + }) output$issue <- DT::renderDT({ DT::datatable(groupidf(), - class = "display", - options = list(pageLength = 6 , - order = list(6 , "desc"), - scrollY = 600, scrollX = 400, scroller = TRUE)) + class = "display", + options = list( + pageLength = 6, + order = list(6, "desc"), + scrollY = 600, scrollX = 400, scroller = TRUE + ) + ) }) - output$dateinferieur <- shiny::renderText({dateinf()}) + output$dateinferieur <- shiny::renderText({ + dateinf() + }) output$activity <- @@ -55,8 +71,8 @@ server <- function(input, output) { final_table, class = "display", options = list( - pageLength = 20 , - order = list(7 , "desc"), + pageLength = 20, + order = list(7, "desc"), scrollY = 2500, scrollX = 800, scroller = TRUE, @@ -65,7 +81,7 @@ server <- function(input, output) { ) }) -# utilisation du jeu de donnée data_chronol qui se trouve dans le global + # utilisation du jeu de donnée data_chronol qui se trouve dans le global filteredData <- reactive({ shiny::req(input$daterange) data_chronol %>% @@ -73,19 +89,21 @@ server <- function(input, output) { }) output$filteredPlot <- plotly::renderPlotly({ - p <- ggplot2::ggplot(filteredData(), ggplot2::aes(x = temps, y = as.factor(name))) + - ggplot2::geom_jitter(ggplot2::aes(shape = type,color = name,text = paste(temps, info, sep = "\n")), - size = 3,width = 0.75,height = 0,alpha = 0.5 + ggplot2::geom_jitter(ggplot2::aes(color = type, text = paste(temps, info, sep = "\n")), + size = 3, alpha = 0.5, + height = 0, width = 0.75 ) + - ggplot2::labs(x = "Temps") + + ggplot2::labs(shape = "Type", color = "Type", x = "") + + ggplot2::scale_x_datetime(date_labels = "%d/%m", timezone = "Europe/Paris") + gouvdown::theme_gouv() + - ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 0, hjust = 1), axis.title.y = ggplot2::element_blank())+ - gouvdown::scale_color_gouv_discrete(palette = "pal_gouv_qual1")+ - scale_shape_manual(values = 1 : 10)+ - ggplot2::scale_x_datetime(date_labels = "%d %b", timezone = "Europe/Paris") + ggplot2::theme( + axis.text.x = ggplot2::element_text(angle = 0, hjust = 1), + axis.title.y = ggplot2::element_blank() + ) + + gouvdown::scale_color_gouv_discrete(palette = "pal_gouv_qual1") + plotly::ggplotly(p, tooltip = "text") }) - } diff --git a/ui.R b/ui.R index c40d7feafcba851f8643e9b6014b46e0fe437728..c9cf7e9404ea9d0979daa47c0f63711f93eea9a0 100644 --- a/ui.R +++ b/ui.R @@ -32,11 +32,11 @@ ui <- shinygouv::navbarPage_dsfr( ), # First tab Projet shinygouv::navbarPanel_dsfr( - title = "Choix du projet à sélectionné", + title = "Choix du projet à sélectionner", shinygouv::fluidRow_dsfr( shinygouv::column_dsfr(12, selectInput("ID1", - label = h3("Nom du projet séléctionner"), - choices = choice + label = h3("Nom du projet séléctionner"), + choices = choice ), extra_class = "fr-my-6w" ) @@ -44,8 +44,8 @@ ui <- shinygouv::navbarPage_dsfr( shinygouv::fluidRow_dsfr(plotlyOutput(outputId = "issue_graph")), shinygouv::fluidRow_dsfr( shinygouv::column_dsfr(12, - DT::DTOutput(outputId = "tablef"), - extra_class = "fr-my-1w" + DT::DTOutput(outputId = "tablef"), + extra_class = "fr-my-1w" ) ) ), @@ -57,10 +57,10 @@ ui <- shinygouv::navbarPage_dsfr( title = "Les issues du projet sélectionné", shinygouv::fluidRow_dsfr( shinygouv::column_dsfr(9, - shiny::textOutput("issue_nbr"), - # Adding space to the column - # https://www.systeme-de-design.gouv.fr/elements-d-interface/fondamentaux-techniques/espacement - extra_class = "fr-my-6w" + shiny::textOutput("issue_nbr"), + # Adding space to the column + # https://www.systeme-de-design.gouv.fr/elements-d-interface/fondamentaux-techniques/espacement + extra_class = "fr-my-6w" ), shinygouv::column_dsfr(3, shinygouv::dateRangeInput_dsfr( inputId = "daterange1", @@ -71,7 +71,7 @@ ui <- shinygouv::navbarPage_dsfr( extra_class = "fr-my-6w" ), shinygouv::column_dsfr(12, DT::DTOutput(outputId = "issue"), - extra_class = "fr-my-1w" + extra_class = "fr-my-1w" ) ) ), @@ -80,7 +80,7 @@ ui <- shinygouv::navbarPage_dsfr( title = "Activité des issues", shinygouv::fluidRow_dsfr( shinygouv::column_dsfr(12, DT::DTOutput(outputId = "activity"), - extra_class = "fr-my-6w" + extra_class = "fr-my-6w" ) ) ), @@ -95,12 +95,12 @@ ui <- shinygouv::navbarPage_dsfr( separator = "à" ), extra_class = "fr-my-6w" - ), + )), + shinygouv::fluidRow_dsfr( shinygouv::column_dsfr(12, plotly::plotlyOutput("filteredPlot", height = "700px"), - extra_class = "fr-my-1w" + extra_class = "fr-my-1w" ) ) ) ) # -