Достаточно часто возникает потребность проведения периодических вычислений и подготовки консолидированного отчета по самодостаточным данным. Т.е. по данным, которые хранятся в виде файлов. Это могут быть данные, набранные из открытых источников, различные документы и excel таблицы, выгрузки из корпоративных систем. Данные в сыром виде могут занимать как несколько мегабайт, так и несколько гигабайт. Данные могут быть обезличенными, либо содержать конфиденциальную информацию. В том случае, когда код вычислений помещается в репозиторий, а работа ведется более чем одним человеком более чем на одном компьютере, возникает проблема сохранения консистентности кода и данных. При этом необходимо еще обеспечить соблюдение разных прав доступа к коду и данным.
Является продолжением предыдущих публикаций.
RStudio сейчас активно разрабатывают пакет pins для решения этой проблемы. К сожалению, применяемые бэкенд решения несколько непопулярны и дороговаты для применения на просторах нашей страны. AWS, Azure, Google cloud… за каждый чих надо платить, и за хранение и за трафик. Аутентификацию AWS4 pins пока не поддерживает, так что Yandex cloud пока тоже в стороне, хотя и он не бесплатен.
С другой стороны, команды аналитиков, работающих над конкретными задачами, как правило, невелики (не более 5-10 человек). Многие используют Google drive, One drive и пр., в платном или бесплатном формате. Почему бы не воспользоваться уже приобретенными ресурсами? Ниже предлагается один из возможных workflow.
Общий план
- Вычисления должны проводиться локально на машине, а значит на машине должна быть актуальная реплика всех необходимых для проведения вычислений данных.
- Код должен быть под системой контроля версий. Данные туда не должны никоим образом попадать (потенциальные объем и конфиденциальность). Будем хранить реплику данных либо в отдельной папке в проекте (включив ее в
.gitignore), либо во внешней относительно проекта директории. - Хранилищем мастер данных будет выступать google drive. Права на доступ к директориям развешиваем в нем же.
Осталось дело за малым. Необходимо реализовать функционал синхронизации локальной реплики данных с облаком. Авторизация и аутентификация обеспечивается google.
Ниже код.
library(googledrive) library(memoise) # синхронизация кэша с google disk drive_user() updateGdCache(here::here("data/"), cloud_folder = "XXX___jZnIW3jdkbdxK0iazx7t63Dc")
updateGdCache <- function(local_folder, cloud_folder){ # обновляем весь кэш одним махом cache_fname <- "gdrive_sig.Rds" # 0. Делаем memoise на загрузку данных из гугла getGdriveFolder <- memoise(function(gdrive_folder){ drive_ls(as_id(gdrive_folder), recursive = FALSE) }) # 1. Проверяем наличие и загружаем облачные идентификаторы указанной папки cloud_gdrive_sig <- purrr::possibly(getGdriveFolder, NULL)(cloud_folder) # Если в облаке ничего нет или связи с ним нет, то и делать дальше нечего if(is.null(cloud_gdrive_sig)) { message("Some Google Drive issues happened. Can't update cache") return() } # 2. Выцепляем директорию и имя файла из пути fdir <- if(fs::is_dir(local_folder)) local_folder else fs::path_dir(local_folder) # 3. Загружаем список локальных файлов на файловой системе local_files <- fs::dir_ls(fdir, recurse = FALSE) %>% fs::path_file() # 4. Проверяем наличие и загружаем локальный кэш облачных идентификаторов local_gdrive_sig <- purrr::possibly(readRDS, NULL, quiet = TRUE)(fs::path(fdir, cache_fname)) if(is.null(local_gdrive_sig)){ # Если локального кэша нет, то сверять нечего, просто загружаем все из облака # сохраняем структуру, удаляем все данные local_gdrive_sig <- cloud_gdrive_sig %>% dplyr::filter(row_number() == -1) } # актуализируем сигнатуры локальных файлов с учетом реально существующих файлов local_gdrive_sig <- local_gdrive_sig %>% dplyr::filter(name %in% local_files) # 5. Сверяем идентификаторы и времена последнего изменения, оставляем файл на обновление, если есть отличия # Облако первично, по нему формируем список файлов для загрузки reconcile_tbl <- cloud_gdrive_sig %>% dplyr::rename(drive_resource_cloud = drive_resource) %>% dplyr::left_join(local_gdrive_sig, by = c("name", "id")) %>% tidyr::hoist(drive_resource_cloud, cloud_modified_time = "modifiedTime") %>% tidyr::hoist(drive_resource, local_modified_time = "modifiedTime") %>% # TODO: надо сверять время, а тут времена изменения выступают как строки # для отсутствующих локальных файлов время модификации = NA dplyr::mutate(not_in_sync = is.na(local_modified_time) | cloud_modified_time != local_modified_time) # 6. Выгружаем файлы на диск syncFile <- function(fpath, id){ res <- purrr::possibly(drive_download, otherwise = NULL)(as_id(id), path = fpath, overwrite = TRUE, verbose = TRUE) ifelse(is.null(res), FALSE, TRUE) } # пакетная загрузка, для сброса сигнатур в кэш оставляем только те, что успешно загрузились sync_gdrive_sig <- reconcile_tbl %>% dplyr::filter(not_in_sync == TRUE) %>% dplyr::mutate(fpath = fs::path(fdir, name)) %>% dplyr::mutate(sync_status = purrr::map2_lgl(fpath, id, syncFile)) %>% dplyr::select(name, id, sync_status) # 7. Сбрасываем в локальный кэш только файлы, находящиеся в синхронизации # Собираем все воедино cloud_gdrive_sig %>% # исключаем ошибочные файлы dplyr::anti_join(dplyr::filter(sync_gdrive_sig, sync_status == FALSE), by = c("name", "id")) %>% saveRDS(fs::path(fdir, cache_fname)) }
В качестве пути указываем идентификатор папки в google drive, можно взять его из адресной строки браузера. Идентификатор будет неизменным, даже если папка будет перемещаться в драйве.

Просто, компактно, удобно и бесплатно.
Пара замечаний
- Есть проблемы с кодировкой у
gargle0.4.0 версии. Надо грузить dev версию. Подробнее здесь. - Есть проблемы с авторизацией на RStudio Server «Unable to authorize from RStudio Server #79 {Closed}», но идеи по обходному пути можно поглядеть здесь.
Предыдущая публикация — «Программирование и новогодняя елка, можно ли их совместить?».
ссылка на оригинал статьи https://habr.com/ru/post/487940/
Добавить комментарий