|
15 | 15 | #' @param timber_path |
16 | 16 | #' character: the file path to the raw timber |
17 | 17 | #' |
18 | | -#' @param cuts |
19 | | -#' named vector: a mapping of the input raw timber names to their standardized column names, |
20 | | -#' produced by \code{set_blade_depth()} |
21 | | -#' |
22 | | -#' @param col_data_types |
23 | | -#' a vector containing the expected data type for each column in the raw timber (i.e. "text"), |
24 | | -#' produced by \code{set_blade_depth()} |
25 | 18 | #' |
26 | 19 | #' @return |
27 | 20 | #' A tibble of timber with standardized column names and additional columns |
|
35 | 28 |
|
36 | 29 |
|
37 | 30 |
|
38 | | -# This function reads the XLSX. Then, it converts cols to standard. Then it sees which are missing. |
| 31 | +debark <- function(timber_path){ |
39 | 32 |
|
40 | | -test_path_csv <- "C:/Users/test/timber.csv" |
41 | | -test_path_xls <- "C:/Users/test/timber.xls" |
42 | | -test_path_xlsx <- "C:/Users/test/timber.xlsx" |
| 33 | + # CHECK: File Type Supported ------------------------------------------------ |
43 | 34 |
|
| 35 | + # Get file extension (lower case for later string comparisons). |
| 36 | + timber_file_ext <- tolower(tools::file_ext(timber_path)) |
44 | 37 |
|
45 | | -# timber_path <- here::here("timber_test.xlsx") |
| 38 | + # Set supported extensions. |
| 39 | + supported_file_exts <- c("csv", "xls", "xlsx") |
46 | 40 |
|
| 41 | + # Abort run if timber file extension is not supported. |
| 42 | + if (! timber_file_ext %in% supported_file_exts) { |
| 43 | + errmsg <- glue::glue("'.{timber_file_ext}' files are not supported by", |
| 44 | + "sawmill. Please specify one of the following file types:", |
| 45 | + paste(supported_file_exts, collapse = ", "), |
| 46 | + ".") |
| 47 | + rlang::abort(message = errmsg) |
| 48 | + } |
47 | 49 |
|
| 50 | + # This abort was previously doubled; asses impact of refactor. |
| 51 | + # rlang::abort(message = errmsg, rlang::abort(message = errmsg)) |
48 | 52 |
|
49 | | -debark2 <- function(timber_path){ |
50 | 53 |
|
51 | | - # Check file type compatibility. |
52 | | - # Future use for import of CSV. |
53 | | - timber_file_type <- get_supported_file_type(timber_path) |
54 | 54 |
|
55 | | - # Read in timber without column specification. |
56 | | - timber_in <- readxl::read_excel(timber_path) |
57 | | - timber_in_col_names <- colnames(timber_in) |
| 55 | + # READ: Raw Timber Specifications ------------------------------------------- |
| 56 | + # The raw timber format (column names and data types) is specified in an |
| 57 | + # external file ("raw_timber_specs.csv"). This file is sparse; read with |
| 58 | + # column specifications to avoid guessing. |
58 | 59 |
|
59 | 60 | # Column types for reading raw_timber_specs |
60 | | - raw_timber_specs_col_types <- readr::cols(timber_col_name = readr::col_character(), |
61 | | - timber_col_required = readr::col_logical(), |
62 | | - sawmill_col_name = readr::col_character(), |
63 | | - col_spec_csv = readr::col_character(), |
64 | | - col_spec_xlsx = readr::col_character(), |
65 | | - timber_obj_name = readr::col_character(), |
66 | | - timber_field_name = readr::col_character(), |
67 | | - timber_field_name_r = readr::col_character()) |
| 61 | + raw_timber_specs_col_types <- readr::cols( |
| 62 | + timber_col_name = readr::col_character(), |
| 63 | + timber_col_required = readr::col_logical(), |
| 64 | + sawmill_col_name = readr::col_character(), |
| 65 | + col_spec_csv = readr::col_character(), |
| 66 | + col_spec_xlsx = readr::col_character(), |
| 67 | + timber_obj_name = readr::col_character(), |
| 68 | + timber_field_name = readr::col_character(), |
| 69 | + timber_field_name_r = readr::col_character()) |
68 | 70 |
|
69 | 71 | # Read raw_timber_specs |
70 | | - raw_timber_specs <- readr::read_csv(file = system.file("raw_timber_specs.csv", package = "sawmill"), |
| 72 | + raw_timber_specs <- readr::read_csv(file = system.file("raw_timber_specs.csv", |
| 73 | + package = "sawmill"), |
71 | 74 | col_types = raw_timber_specs_col_types) |
72 | 75 |
|
73 | | - # Get the names of required columns. |
74 | | - raw_timber_req_col_names <- raw_timber_specs$timber_col_name[raw_timber_specs$timber_col_required] |
75 | | - |
76 | | - # Are required columns present in the input timber? |
77 | | - raw_timber_req_cols_here <- raw_timber_req_col_names %in% timber_in_col_names |
78 | | - |
79 | | - # If any required columns are missing, abort. |
80 | | - if(any(!raw_timber_req_cols_here)){ |
81 | | - rlang::abort(message = glue::glue("Column '{raw_timber_req_col_names[!raw_timber_req_cols_here]}' is missing or improperly named.")) |
82 | | - } |
83 | | - |
84 | | - # Create a column specification for raw timber. |
85 | | - raw_timber_colspec <- rlang::set_names(raw_timber_specs$col_spec_xlsx, raw_timber_specs$timber_col_name) |
86 | 76 |
|
87 | | - # Create a column specification for the input timber. Default = "guess". |
88 | | - timber_in_colspec <- rlang::set_names(rep("guess", length(timber_in_col_names)), timber_in_col_names) |
89 | 77 |
|
90 | | - # Replace the column specification for the input timber for the required fields in raw timber. |
91 | | - timber_in_colspec[intersect(names(raw_timber_colspec), names(timber_in_colspec))] <- raw_timber_colspec[intersect(names(raw_timber_colspec), names(timber_in_colspec))] |
92 | | - |
93 | | - # Re-read the timber with column specification. |
94 | | - timber_in <- readxl::read_excel(timber_path, col_types = timber_in_colspec) |
95 | | - |
96 | | - |
97 | | - # Get the old column names (sawmill). |
98 | | - raw_timber_req_col_names_old <- raw_timber_specs$sawmill_col_name[raw_timber_specs$timber_col_required] |
99 | | - |
100 | | - # Map the old names onto the new names. |
101 | | - names(raw_timber_req_col_names) <- raw_timber_req_col_names_old |
| 78 | + # CHECK: Required Columns Exist --------------------------------------------- |
| 79 | + # Check if requisite columns exist, abort if requisite columns are missing. |
102 | 80 |
|
103 | | - # Rename required columns. |
104 | | - timber_in <- rename(timber_in, raw_timber_req_col_names) |
| 81 | + # Get the names of required columns. |
| 82 | + raw_timber_req_col_names <- raw_timber_specs$timber_col_name[raw_timber_specs$timber_col_required] |
105 | 83 |
|
106 | | - if(!"exclude_sawmill" %in% timber_in_col_names){timber_in$exclude_sawmill <- FALSE} |
107 | | - if(!"exclude_sawmill_reason" %in% timber_in_col_names){timber_in$exclude_sawmill_reason <- NA} |
| 84 | + # Read the header of the raw timber to get passed column names. |
| 85 | + if (timber_file_ext == 'csv') { |
108 | 86 |
|
109 | | - return(timber_in) |
| 87 | + timber_passed_col_names <- names(readr::read_csv(file = timber_path, |
| 88 | + n_max = 0, |
| 89 | + show_col_types = FALSE)) |
110 | 90 |
|
111 | | -} |
| 91 | + } else if (timber_file_ext %in% c("xls", "xlsx")) { |
112 | 92 |
|
| 93 | + timber_passed_col_names <- names(readxl::read_excel(path = timber_path, |
| 94 | + n_max = 0)) |
113 | 95 |
|
114 | | -get_supported_file_type <- function(path){ |
| 96 | + } |
115 | 97 |
|
116 | | - extn <- tools::file_ext(path) |
117 | | - errmsg <- glue::glue("Filetype '.{extn}' is not supported. Please use .xlsx.") |
| 98 | + # Are required columns present? |
| 99 | + raw_timber_req_cols_here <- raw_timber_req_col_names %in% timber_passed_col_names |
118 | 100 |
|
119 | | - ifelse(extn %in% c("xls", "xlsx"), "xlsx", |
120 | | - ifelse(extn == "csv", rlang::abort(message = errmsg, rlang::abort(message = errmsg)))) |
| 101 | + # Abort run if required columns are missing. |
| 102 | + if(any(!raw_timber_req_cols_here)){ |
| 103 | + rlang::abort(message = glue::glue("Columns '{raw_timber_req_col_names[!raw_timber_req_cols_here]}' is missing or improperly named.")) |
| 104 | + } |
121 | 105 |
|
122 | | -} |
123 | 106 |
|
124 | | -#get_supported_file_type(test_path_xlsx) |
125 | | -#get_supported_file_type(test_path_csv) |
126 | 107 |
|
127 | | -#timber_col_name timber_col_required sawmill_col_name col_spec_csv col_spec_xlsx |
| 108 | + # READ: Timber -------------------------------------------------------------- |
| 109 | + # Create a named vector of column specifications for known columns. |
| 110 | + # Timber may contain unknown columns; create a named vector of default (guess) |
| 111 | + # specifications for passed timber. Then, replace guess for known columns. |
128 | 112 |
|
| 113 | + if (timber_file_ext == 'csv') { |
129 | 114 |
|
| 115 | + # Create a named vector of column specifications (CSV) for raw timber. |
| 116 | + raw_timber_colspec <- rlang::set_names(raw_timber_specs$col_spec_csv, |
| 117 | + raw_timber_specs$timber_col_name) |
130 | 118 |
|
| 119 | + # Create a column specification for the input timber. Default = "?". |
| 120 | + timber_in_colspec <- rlang::set_names(rep("?", length(timber_passed_col_names)), |
| 121 | + timber_passed_col_names) |
131 | 122 |
|
| 123 | + # Replace the column specification for the input timber for the required fields in raw timber. |
| 124 | + timber_in_colspec[intersect(names(raw_timber_colspec), names(timber_in_colspec))] <- raw_timber_colspec[intersect(names(raw_timber_colspec), names(timber_in_colspec))] |
132 | 125 |
|
| 126 | + # Re-read the timber with column specification. |
| 127 | + timber_in <- readr::read_csv(file = timber_path, |
| 128 | + col_types = timber_in_colspec, |
| 129 | + show_col_types = FALSE) |
133 | 130 |
|
| 131 | + } else if (timber_file_ext %in% c("xls", "xlsx")) { |
134 | 132 |
|
| 133 | + # Create a named vector of column specifications (XLS/X) for raw timber. |
| 134 | + raw_timber_colspec <- rlang::set_names(raw_timber_specs$col_spec_xlsx, |
| 135 | + raw_timber_specs$timber_col_name) |
135 | 136 |
|
136 | | -debark <- function(timber_path, cuts, col_data_types){ |
| 137 | + # Create a column specification for the input timber. Default = "guess". |
| 138 | + timber_in_colspec <- rlang::set_names(rep("guess", length(timber_passed_col_names)), |
| 139 | + timber_passed_col_names) |
137 | 140 |
|
138 | | - raw_timber <- readxl::read_excel(timber_path) |
| 141 | + # Replace the column specification for the input timber for the required fields in raw timber. |
| 142 | + timber_in_colspec[intersect(names(raw_timber_colspec), names(timber_in_colspec))] <- raw_timber_colspec[intersect(names(raw_timber_colspec), names(timber_in_colspec))] |
139 | 143 |
|
140 | | - # Screen for missing columns (report which ones are missing) |
141 | | - missing <- setdiff(as.vector(cuts), names(raw_timber)) |
| 144 | + # Re-read the timber with column specification. |
| 145 | + timber_in <- readxl::read_excel(path = timber_path, |
| 146 | + col_types = timber_in_colspec) |
142 | 147 |
|
143 | | - if(length(missing)>0) { |
144 | | - stop(paste('ERROR: The following requisite columns are missing from your timber: ', |
145 | | - paste(missing, collapse = ", ") |
146 | | - ) |
147 | | - ) |
148 | 148 | } |
149 | 149 |
|
150 | | - # Screen for cells that do not match the expected data type for their column(s) |
151 | 150 |
|
152 | | - sub_mill(log_warnings(raw_timber <- readxl::read_excel(timber_path, col_types = col_data_types)), "log_warnings") |
153 | 151 |
|
154 | | - tryCatch(raw_timber <- readxl::read_excel(timber_path, col_types = col_data_types), |
155 | | - warning = function(w) { |
156 | | - sub_mill(handle_col_warnings(), "handle_col_warnings") |
157 | | - }) |
| 152 | + # Remap Names --------------------------------------------------------------- |
158 | 153 |
|
| 154 | + # Get the old column names (sawmill). |
| 155 | + raw_timber_req_col_names_old <- raw_timber_specs$sawmill_col_name[raw_timber_specs$timber_col_required] |
159 | 156 |
|
160 | | - timber <- dplyr::rename(raw_timber, cuts) |
| 157 | + # Map the old names onto the new names. |
| 158 | + names(raw_timber_req_col_names) <- raw_timber_req_col_names_old |
161 | 159 |
|
| 160 | + # Rename required columns. |
| 161 | + timber_in <- dplyr::rename(timber_in, dplyr::all_of(raw_timber_req_col_names)) |
162 | 162 |
|
163 | | - # Add exclude_sawmill and exclude_sawmill_reason columns to indicate factors |
164 | | - # to omit from further processing. |
165 | 163 |
|
166 | | - timber$exclude_sawmill <- FALSE |
167 | | - timber$exclude_sawmill_reason <- NA |
168 | 164 |
|
169 | | - return(timber) |
| 165 | + # Add Extra Columns --------------------------------------------------------- |
170 | 166 |
|
171 | | -} |
| 167 | + if(!"ID_meta" %in% timber_passed_col_names){timber_in$ID_meta <- NA_integer_} |
| 168 | + if(!"exclude_sawmill" %in% timber_passed_col_names){timber_in$exclude_sawmill <- FALSE} |
| 169 | + if(!"exclude_sawmill_reason" %in% timber_passed_col_names){timber_in$exclude_sawmill_reason <- NA} |
172 | 170 |
|
173 | 171 |
|
174 | 172 |
|
| 173 | + return(timber_in) |
175 | 174 |
|
| 175 | +} |
176 | 176 |
|
177 | 177 |
|
0 commit comments