Skip to content

Improving load_aneel() #313

Description

@bernardo-sieira

About the energy_development_budget dataset:

We used to download the data from a googledrive directory, now unavaiable.
Considering we are trying to submit and update the package version on CRAN, the solution being adopted is simple and temporary. We will solely provide the CDE data as in the .csv files avaibale here. They are anual and only cover Rede-Aberta.

After the CRAN submission (and approval), it is proposed to implement a broader function with more options.

I recommend to give the user a new input ithat refers to the months wanted (to be used paired to the years) :

MONTH-YEAR

The function must read the .zip files avaible at the same address. A reading code could possibly be the following:

download_energy_development_budget <- function(years = NULL, months = NULL) {
  # Default months to all
  if (is.null(months)) months <- 1:12

  # Portuguese month abbreviations used in the file names
  month_abbr_pt <- c(
    "jan", "fev", "mar", "abr", "mai", "jun",
    "jul", "ago", "set", "out", "nov", "dez"
  )

  ## Query the CKAN API to get all resources for this dataset
  api_url <- "https://dadosabertos.aneel.gov.br/api/3/action/package_show"
  response <- httr::GET(api_url, query = list(id = "beneficiarios-da-cde"))

  if (httr::status_code(response) != 200) {
    stop("Failed to query ANEEL open data API. HTTP status: ", httr::status_code(response))
  }

  api_data <- httr::content(response, as = "parsed", simplifyVector = TRUE)
  resources <- api_data$result$resources

  # Keep only resources that have a download URL ending in .zip or .csv
  resources <- resources[grepl("\\.(zip|csv)$", resources$url, ignore.case = TRUE), ]

  if (nrow(resources) == 0) {
    stop("No downloadable resources found for the energy_development_budget dataset on ANEEL open data portal.")
  }

  ## Parse year and month from resource names/URLs
  # File names follow patterns like: cde-beneficiarios-01jan2024.zip
  # or may contain year info in the name field
  resources$parsed_year <- NA_integer_
  resources$parsed_month <- NA_integer_

  for (i in seq_len(nrow(resources))) {
    url_lower <- tolower(resources$url[i])
    name_lower <- tolower(resources$name[i])
    text <- paste(url_lower, name_lower)

    # Try to match pattern like "01jan2024" or "jan2024" or "012024"
    for (m in seq_along(month_abbr_pt)) {
      pattern <- paste0("(\\d{0,2})", month_abbr_pt[m], "(\\d{4})")
      match <- regmatches(text, regexpr(pattern, text))
      if (length(match) > 0 && nchar(match) > 0) {
        yr <- as.integer(sub(paste0(".*", month_abbr_pt[m], "(\\d{4}).*"), "\\1", match))
        resources$parsed_year[i] <- yr
        resources$parsed_month[i] <- m
        break
      }
    }

    # Fallback: try to extract a 4-digit year from the name if not yet parsed
    if (is.na(resources$parsed_year[i])) {
      yr_match <- regmatches(text, regexpr("\\b(20\\d{2})\\b", text))
      if (length(yr_match) > 0 && nchar(yr_match) > 0) {
        resources$parsed_year[i] <- as.integer(yr_match)
      }
    }
  }

  # Remove resources we couldn't parse a year for
  resources <- resources[!is.na(resources$parsed_year), ]

  # Default years to all available if NULL
  if (is.null(years)) {
    years <- sort(unique(resources$parsed_year))
  }

  # Filter to requested years and months
  resources <- resources[
    resources$parsed_year %in% years &
      (is.na(resources$parsed_month) | resources$parsed_month %in% months),
  ]

  if (nrow(resources) == 0) {
    stop(
      "No resources found for the requested years (",
      paste(years, collapse = ", "),
      ") and months (",
      paste(months, collapse = ", "),
      "). Check available data at https://dadosabertos.aneel.gov.br/dataset/beneficiarios-da-cde"
    )
  }

  message(
    "Downloading ", nrow(resources),
    " file(s) from ANEEL open data portal. This may take a while."
  )

  ## Download and read each resource
  dir <- tempdir()

  dat_list <- purrr::map(seq_len(nrow(resources)), function(idx) {
    resource_url <- resources$url[idx]
    file_extension <- tolower(sub(".*\\.", ".", resource_url))

    temp <- tempfile(fileext = file_extension, tmpdir = dir)

    tryCatch(
      {
        utils::download.file(url = resource_url, destfile = temp, mode = "wb", quiet = TRUE)

        if (file_extension == ".zip") {
          # Unzip and read CSV(s) inside
          exdir <- file.path(dir, paste0("cde_", idx))
          dir.create(exdir, showWarnings = FALSE, recursive = TRUE)
          utils::unzip(temp, exdir = exdir)

          csv_files <- list.files(exdir, pattern = "\\.csv$", full.names = TRUE, recursive = TRUE)
          if (length(csv_files) == 0) {
            warning("No CSV found inside ZIP: ", resource_url)
            return(NULL)
          }

          dfs <- lapply(csv_files, function(f) {
            tryCatch(
              data.table::fread(f, encoding = "Latin-1"),
              error = function(e) {
                warning("Error reading ", f, ": ", e$message)
                NULL
              }
            )
          })

          dfs <- dfs[!sapply(dfs, is.null)]
          if (length(dfs) == 0) return(NULL)
          do.call(rbind, dfs)
        } else {
          # Assume CSV
          data.table::fread(temp, encoding = "Latin-1")
        }
      },
      error = function(e) {
        warning("Failed to download/read: ", resource_url, " - ", e$message)
        NULL
      }
    )
  })

  # Remove NULLs and bind
  dat_list <- dat_list[!sapply(dat_list, is.null)]

  if (length(dat_list) == 0) {
    stop("All downloads failed. Please check your internet connection and try again.")
  }

  dat <- data.table::rbindlist(dat_list, fill = TRUE) %>%
    tibble::as_tibble()

  message("Successfully loaded ", nrow(dat), " rows from ", length(dat_list), " file(s).")

  return(dat)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions