|
| 1 | +<!-- |
| 2 | +# @markup markdown |
| 3 | +# @title FCAR recipes |
| 4 | +--> |
| 5 | + |
| 6 | +# FCAR (facilitated cleanup and remapping) recipes |
| 7 | + |
| 8 | +This page documents common patterns of client FCAR using kiba-extend's iterative cleanup functionality. |
| 9 | + |
| 10 | +Each recipe has three components: |
| 11 | + |
| 12 | + - Prep/setup job - the structure of the data required as input for the FCAR process, and any transforms that exist to streamline achieving this structure |
| 13 | + - FCAR configuration - A commented version of the configuration Module to include in your project to activate this FCAR |
| 14 | + - Merge job - patterns for merging the FCAR back into the rest of your project |
| 15 | + |
| 16 | +**Table of contents** |
| 17 | + |
| 18 | +- [Review and correction of programmatic value splitting](#review-and-correction-of-programmatic-value-splitting) |
| 19 | + * [Prep/setup job](#prepsetup-job) |
| 20 | + + [Normalization job example](#normalization-job-example) |
| 21 | + + [Prep example](#prep-example) |
| 22 | + * [FCAR configuration](#fcar-configuration) |
| 23 | + * [Merge job](#merge-job) |
| 24 | + |
| 25 | +## Review and correction of programmatic value splitting |
| 26 | + |
| 27 | +The prep and merge sections below use `:init__prep` as the job output from which we are peeling off this FCAR proccess, and thus to which we are merging its results back in. |
| 28 | + |
| 29 | +### Prep/setup job |
| 30 | + |
| 31 | +To ease the merge process, it's recommended you break this into two jobs: |
| 32 | + |
| 33 | +- normalize: normalizes the values to be included in the worksheet |
| 34 | +- prep: deduplicates on normalized values and finalizes prep for the split FCAR |
| 35 | + |
| 36 | +#### Normalization job example |
| 37 | + |
| 38 | +In this example, we are pulling just the location field values out of a single migrating table and applying [the normalization described in the worksheet instructions](https://github.com/lyrasis/kiba-extend/blob/main/fcar_instructions/split_values.adoc#details-on-data-preprocessing-done-prior-to-preparing-this-worksheet) to them. |
| 39 | + |
| 40 | +~~~ ruby |
| 41 | +# frozen_string_literal: true |
| 42 | + |
| 43 | +module Project |
| 44 | + module Jobs |
| 45 | + module ValueSplit |
| 46 | + module FcarNorm |
| 47 | + module_function |
| 48 | + |
| 49 | + def job |
| 50 | + Kiba::Extend::Jobs::Job.new( |
| 51 | + files: { |
| 52 | + source: :init__prep, |
| 53 | + destination: :value_split__fcar_norm |
| 54 | + }, |
| 55 | + transformer: xforms |
| 56 | + ) |
| 57 | + end |
| 58 | + |
| 59 | + def xforms |
| 60 | + Kiba.job_segment do |
| 61 | + transform Delete::FieldsExcept, |
| 62 | + fields: %i[location] |
| 63 | + transform FilterRows::FieldPopulated, |
| 64 | + action: :keep, |
| 65 | + field: :location |
| 66 | + transform Deduplicate::Table, |
| 67 | + field: :location |
| 68 | + |
| 69 | + # Adjust the normalization in a way that makes sense for the data. |
| 70 | + # We want to be as aggressive as we can in normalizing, without |
| 71 | + # starting to over-lump things that should be kept discrete |
| 72 | + transform Normalize::FieldValues, |
| 73 | + fields: :location, |
| 74 | + targets: :norm, |
| 75 | + xforms: [:lower], |
| 76 | + replacements: { |
| 77 | + / +/ => " ", |
| 78 | + /^ / => "", |
| 79 | + / $/ => "" |
| 80 | + } |
| 81 | + transform Replace::NormWithMostFrequentlyUsedForm, |
| 82 | + normfield: :norm, |
| 83 | + nonnormfield: :location, |
| 84 | + target: :normloc |
| 85 | + transform Delete::Fields, |
| 86 | + fields: :norm |
| 87 | + end |
| 88 | + end |
| 89 | + end |
| 90 | + end |
| 91 | + end |
| 92 | +end |
| 93 | +~~~ |
| 94 | + |
| 95 | +#### Prep example |
| 96 | + |
| 97 | +~~~ ruby |
| 98 | +# frozen_string_literal: true |
| 99 | + |
| 100 | +module Project |
| 101 | + module Jobs |
| 102 | + module ValueSplit |
| 103 | + module FcarPrep |
| 104 | + module_function |
| 105 | + |
| 106 | + def job |
| 107 | + Kiba::Extend::Jobs::Job.new( |
| 108 | + files: { |
| 109 | + source: :value_split__fcar_norm, |
| 110 | + destination: :value_split__fcar_prep |
| 111 | + }, |
| 112 | + transformer: xforms |
| 113 | + ) |
| 114 | + end |
| 115 | + |
| 116 | + def xforms |
| 117 | + Kiba.job_segment do |
| 118 | + transform Deduplicate::Table, |
| 119 | + field: :normloc, |
| 120 | + include_occs: true, |
| 121 | + compile_uniq_fieldvals: true |
| 122 | + transform Rename::Fields, fieldmap: { |
| 123 | + location: :unnormalizedlocations |
| 124 | + } |
| 125 | + |
| 126 | + # Set up the splitters you need here |
| 127 | + transform Fcar::SplitPrep, |
| 128 | + orig: :normloc, |
| 129 | + splitters: { |
| 130 | + / *; */ => :semicolon, |
| 131 | + / and /i => :and, |
| 132 | + / & / => :ampersand |
| 133 | + } |
| 134 | + transform Sort::ByFieldValue, |
| 135 | + field: :sort, |
| 136 | + mode: :string |
| 137 | + end |
| 138 | + end |
| 139 | + end |
| 140 | + end |
| 141 | + end |
| 142 | +end |
| 143 | +~~~ |
| 144 | + |
| 145 | +### FCAR configuration |
| 146 | + |
| 147 | +~~~ ruby |
| 148 | +# frozen_string_literal: true |
| 149 | + |
| 150 | +module Project |
| 151 | + module ValueSplit |
| 152 | + module_function |
| 153 | + |
| 154 | + # Most of these settings/variables are documented in: |
| 155 | + # https://lyrasis.github.io/kiba-extend/Kiba/Extend/Mixins/IterativeCleanup.html |
| 156 | + |
| 157 | + # Job key of the prep job to be used as input for the FCAR. Change this |
| 158 | + # to whatever you have named the job in your project. |
| 159 | + def base_job = :value_type__split_prep |
| 160 | + |
| 161 | + # Don't change this without good reason. The values used to uniquely |
| 162 | + # identify a corrected worksheet row |
| 163 | + def fingerprint_fields = %i[split_val orig] |
| 164 | + |
| 165 | + extend Kiba::Extend::Mixins::IterativeCleanup |
| 166 | + |
| 167 | + def orig_values_identifier = :prepped_row_fingerprint |
| 168 | + |
| 169 | + # Edit this to work with the tags in your project |
| 170 | + def job_tags = %i[value_type split cleanup] |
| 171 | + |
| 172 | + # Edit this if your worksheet data includes other headers you wish to |
| 173 | + # include in the ordering |
| 174 | + def worksheet_field_order = %i[split_val orig split to_review |
| 175 | + sort] |
| 176 | + |
| 177 | + # Delete this if you aren't including an occurrences field or other |
| 178 | + # field that should be collated. These include any fields that indicate |
| 179 | + # in what field(s) a term was used; the unnormalized forms of name that |
| 180 | + # may have been normalized to create the "orig" value for the FCAR |
| 181 | + # process, etc. |
| 182 | + def collate_fields = %i[occurrences] |
| 183 | + |
| 184 | + # Delete this if you aren't including a numeric occurrences collated field |
| 185 | + # that needs to be summed. |
| 186 | + def cleaned_uniq_post_xforms |
| 187 | + bind = binding |
| 188 | + |
| 189 | + Kiba.job_segment do |
| 190 | + mod = bind.receiver |
| 191 | + |
| 192 | + transform Kiba::Extend::Transforms::Fcar::Helpers::SumCollatedOccurrences, |
| 193 | + field: :occurrences, |
| 194 | + delim: mod.collation_delim |
| 195 | + end |
| 196 | + end |
| 197 | + |
| 198 | + def final_post_xforms |
| 199 | + Kiba.job_segment do |
| 200 | + # Get rid of worksheet fields required for merging back into project |
| 201 | + # that could have been modified by client, and the helper |
| 202 | + # `autosplit` column |
| 203 | + transform Delete::Fields, |
| 204 | + fields: %i[orig sort autosplit] |
| 205 | + # Reconstitute the original values of fields critical for merging from |
| 206 | + # the prepped row fingerprint, and delete the fingerprint field, as |
| 207 | + # it has served its purpose |
| 208 | + transform Fingerprint::Decode, |
| 209 | + fingerprint: :prepped_row_fingerprint, |
| 210 | + source_fields: %i[orig split_val sort], |
| 211 | + delete_fp: true |
| 212 | + transform Rename::Fields, fieldmap: { |
| 213 | + fp_orig: :orig, |
| 214 | + fp_sort: :sort |
| 215 | + } |
| 216 | + # We don't need the uncorrected `split_val` values from the |
| 217 | + # fingerprint |
| 218 | + transform Delete::Fields, |
| 219 | + fields: :fp_split_val |
| 220 | + # Drop rows where client has deleted values from `split_val` |
| 221 | + transform FilterRows::FieldPopulated, |
| 222 | + action: :keep, |
| 223 | + field: :split_val |
| 224 | + # This and the following Deduplicate::Table step exist to |
| 225 | + # prevent duplicate values being merged into the project |
| 226 | + # if/when client has entered corrected split on all |
| 227 | + # rows for the original data |
| 228 | + transform CombineValues::FromFieldsWithDelimiter, |
| 229 | + sources: %i[orig split_val], |
| 230 | + target: :combined, |
| 231 | + delete_sources: false, |
| 232 | + delim: " " |
| 233 | + transform Deduplicate::Table, |
| 234 | + field: :combined, |
| 235 | + delete_field: true |
| 236 | + # Set up so merging will keep values in their original order |
| 237 | + transform Sort::ByFieldValue, |
| 238 | + field: :sort, |
| 239 | + mode: :string |
| 240 | + end |
| 241 | + end |
| 242 | + |
| 243 | + def final_lookup_on_field = :orig |
| 244 | + end |
| 245 | +end |
| 246 | +~~~ |
| 247 | + |
| 248 | +### Merge job |
| 249 | + |
| 250 | +This job replaces the `location` values in the original `:init__prep` output with the correctly and unambiguously delimited values from the FCAR worksheet. |
| 251 | + |
| 252 | +~~~ ruby |
| 253 | +# frozen_string_literal: true |
| 254 | + |
| 255 | +module Project |
| 256 | + module Jobs |
| 257 | + module ValueSplit |
| 258 | + module FcarMerge |
| 259 | + module_function |
| 260 | + |
| 261 | + def job |
| 262 | + Kiba::Extend::Jobs::Job.new( |
| 263 | + files: { |
| 264 | + source: :init__prep, |
| 265 | + destination: :value_split__fcar_merge, |
| 266 | + lookup: [ |
| 267 | + {jobkey: :value_split__fcar_norm, lookup_on: :location}, |
| 268 | + {jobkey: :value_split__final, lookup_on: :orig} |
| 269 | + ] |
| 270 | + }, |
| 271 | + transformer: xforms |
| 272 | + ) |
| 273 | + end |
| 274 | + |
| 275 | + def xforms |
| 276 | + Kiba.job_segment do |
| 277 | + # First, merge the normalized form of each location into the table |
| 278 | + transform Merge::MultiRowLookup, |
| 279 | + lookup: loc_split__fcar_norm, |
| 280 | + keycolumn: :location, |
| 281 | + fieldmap: {normloc: :normloc} |
| 282 | + |
| 283 | + # Delete the old location field once normalized forms are merged in, since |
| 284 | + # we are replacing with correct forms in a minute |
| 285 | + transform Delete::Fields, fields: :location |
| 286 | + |
| 287 | + # Merge in corrected values, matching on the normalized locations we just |
| 288 | + # merged in, and the normalized locations in the "orig" column of the FCAR |
| 289 | + transform Merge::MultiRowLookup, |
| 290 | + lookup: loc_split__final, |
| 291 | + keycolumn: :normloc, |
| 292 | + fieldmap: {location: :split_val}, |
| 293 | + delim: Sr.delim |
| 294 | + |
| 295 | + # We don't need to keep the normalized location now that we've matched on it |
| 296 | + transform Delete::Fields, |
| 297 | + fields: :normloc |
| 298 | + end |
| 299 | + end |
| 300 | + end |
| 301 | + end |
| 302 | + end |
| 303 | +end |
| 304 | + |
| 305 | +~~~ |
0 commit comments