@@ -22,8 +22,8 @@ const getFieldNameMap = (fields) => {
22
22
* This code works on exportHeaders as either a Map or an array of
23
23
* [['field_name',[fields],...]
24
24
* @param {Array } exportHeaders See `export.js`.
25
- * @param {Array<Object> } array of all source fields.
26
- * @param {String } export column prefix
25
+ * @param {Array<Object> } fields array of all source fields.
26
+ * @param {String } prefix export column prefix
27
27
*/
28
28
const getHeaderMap = ( exportHeaders , fields , prefix ) => {
29
29
var headerMap = { } ;
@@ -44,51 +44,153 @@ const getHeaderMap = (exportHeaders, fields, prefix) => {
44
44
}
45
45
}
46
46
47
+ let field_message = [ ] ;
48
+ let field_export_message = [ ] ;
49
+
47
50
for ( const [ fieldIndex , field ] of fields . entries ( ) ) {
48
51
if ( field . exportField && prefix in field . exportField ) {
49
52
for ( const target of field . exportField [ prefix ] ) {
50
53
if ( 'field' in target ) {
51
- if ( target . field in headerMap ) {
52
- var sources ;
53
- if ( exportHeaders instanceof Map ) {
54
- sources = exportHeaders . get ( target . field ) ;
55
- // If given field isn't already mapped, add it.
56
- if ( sources . indexOf ( field . fieldName ) == - 1 ) {
57
- sources . push ( field . fieldName ) ;
58
- } ;
59
- exportHeaders . set ( target . field , sources ) ;
54
+ var sources ;
55
+ if ( exportHeaders instanceof Map ) {
56
+ if ( target . field in headerMap ) {
57
+ field_export_message . push ( target . field ) ;
58
+ }
59
+ else {
60
+ if ( ! exportHeaders . has ( target . field ) ) {
61
+ field_message . push ( target . field ) ;
62
+ // Issue: all template driven exportHeader fields are showing AFTER export.js mentioned ones.
63
+ headerMap [ target . field ] = exportHeaders . length ;
64
+ exportHeaders . set ( target . field , [ ] ) ;
65
+ }
60
66
}
61
- else { // Save to array
62
- sources = exportHeaders [ headerMap [ target . field ] ] [ 1 ] ;
63
- // As above
64
- if ( sources . indexOf ( field . fieldName ) == - 1 ) {
65
- sources . push ( field . fieldName ) ;
66
- } ;
67
- exportHeaders [ headerMap [ target . field ] ] [ 1 ] = sources ;
67
+ let sources = exportHeaders . get ( target . field ) ;
68
+ if ( ! sources )
69
+ console . log ( 'Malformed export.js exportHeader field:' , target . field )
70
+ // If given field isn't already mapped, add it.
71
+ if ( sources . indexOf ( field . fieldName ) == - 1 ) {
72
+ sources . push ( field . fieldName ) ;
68
73
} ;
74
+ exportHeaders . set ( target . field , sources ) ;
69
75
}
70
- else {
71
- const msg = 'The EXPORT_' + prefix + ' column for ' + field . fieldName + ' requests a map to a non-existen export template field: ' + target . field ;
72
- console . log ( msg ) ;
76
+ else { // Save to array
77
+ if ( target . field in headerMap ) {
78
+ field_export_message . push ( target . field ) ;
79
+ }
80
+ else {
81
+ // Add field to exportHeaders
82
+ // Issue: can this handle many-to-one mapping?
83
+ field_message . push ( target . field ) ;
84
+ headerMap [ target . field ] = exportHeaders . length ;
85
+ exportHeaders . push ( [ target . field , [ ] ] ) ;
86
+ }
87
+ sources = exportHeaders [ headerMap [ target . field ] ] [ 1 ] ;
88
+ // As above
89
+ if ( sources . indexOf ( field . fieldName ) == - 1 ) {
90
+ sources . push ( field . fieldName ) ;
91
+ } ;
92
+ exportHeaders [ headerMap [ target . field ] ] [ 1 ] = sources ;
73
93
} ;
94
+
74
95
} ;
75
96
} ;
76
97
} ;
77
98
} ;
99
+ // This will output a list of fields added to exportHeaders by way of template specification which haven't been included in export.js
100
+ if ( field_message )
101
+ console . log ( 'Export fields added by template:' , field_message )
102
+ if ( field_export_message )
103
+ console . log ( 'Export fields stated in export.js):' , field_export_message )
78
104
} ;
79
105
80
- const getMappedField = ( sourceRow , sourceFieldNames , fieldNameMap , delimiter ) => {
81
- // This provides an export field composed of one or more more input
82
- // fields, separated by a ';' delimiter if not null.
106
+ /**
107
+ * This provides an export field composed of one or more more input
108
+ * fields, separated by a ';' delimiter if not null.
109
+ * nullOptionsDict allows conversion of "Missing" etc. metadata options to
110
+ * target export system's version of these.
111
+ * @param {Object } sourceRow
112
+ * @param {Array<Object> } sourceFieldNames array of all source fields.
113
+ * @param {Object } fieldNameMap
114
+ * @param {String } delimiter to separate multi-source field values with
115
+ * @param {String } prefix of export format
116
+ * @param {Map } nullOptionsMap conversion of Missing etc. to export db equivalent.
117
+ * @returm {String} Concatenated string of values.
118
+ */
119
+ const getMappedField = ( sourceRow , sourceFieldNames , sourceFields , fieldNameMap , delimiter , prefix , nullOptionsMap = null ) => {
120
+
83
121
const mappedCell = [ ] ;
84
122
for ( const fieldName of sourceFieldNames ) {
85
- const mappedCellVal = sourceRow [ fieldNameMap [ fieldName ] ] ;
123
+ let mappedCellVal = sourceRow [ fieldNameMap [ fieldName ] ] ;
86
124
if ( ! mappedCellVal ) continue ;
87
- mappedCell . push ( mappedCellVal ) ;
125
+ mappedCellVal = mappedCellVal . trim ( ) ;
126
+ if ( mappedCellVal . length === 0 ) continue ;
127
+ if ( nullOptionsMap && nullOptionsMap . has ( mappedCellVal ) ) {
128
+ mappedCellVal = nullOptionsMap . get ( mappedCellVal ) ;
129
+ } ;
130
+ let field = sourceFields [ fieldNameMap [ fieldName ] ] ;
131
+ if ( field . datatype === 'select' ) {
132
+ mappedCell . push ( getTransformedField ( mappedCellVal , field , prefix ) ) ;
133
+ }
134
+ else if ( field . datatype === 'multiple' ) {
135
+ // ISSUE: relying on semicolon delimiter in input
136
+ for ( let cellVal of mappedCellVal . split ( ';' ) ) {
137
+ mappedCell . push ( getTransformedField ( cellVal . trim ( ) , field , prefix ) ) ;
138
+ }
139
+ }
140
+ else {
141
+ mappedCell . push ( mappedCellVal )
142
+ }
88
143
} ;
89
144
return mappedCell . join ( delimiter ) ;
90
145
}
91
146
147
+ /**
148
+ * Some vocabulary fields get mapped over to export format values.
149
+ *
150
+ * @param {String } value to be exported.
151
+ * @param {Array<String> } fields list of source fields to examine for mappings.
152
+ * @param {String } prefix of export format to examine.
153
+ */
154
+ const getTransformedField = ( value , field , prefix ) => {
155
+
156
+ if ( field [ 'schema:ItemList' ] ) {
157
+ const term = findById ( field [ 'schema:ItemList' ] , value ) ;
158
+
159
+ // Looking for term.exportField['GRDI'] for example:
160
+ if ( term && 'exportField' in term && prefix in term . exportField ) {
161
+ // Here mapping involves a value substitution
162
+ // Note possible [target field]:[value] twist
163
+ for ( let mapping of term . exportField [ prefix ] ) {
164
+ return mapping . value ;
165
+ } ;
166
+ } ;
167
+
168
+ } ;
169
+ return value ;
170
+ } ;
171
+
172
+ /* Find key in nested object (nested dictionaries)
173
+ * Adapted from: https://codereview.stackexchange.com/questions/73714/find-a-nested-property-in-an-object
174
+ * @param {Dictionary<Dictionary> } o nested Dictionaries
175
+ * @param {String }Key to find in dictionaries
176
+ * @return {Dictionary } or null
177
+ */
178
+ function findById ( o , key ) {
179
+ if ( key in o )
180
+ return o [ key ] ;
181
+ var result , p ;
182
+ for ( p in o ) {
183
+ if ( o . hasOwnProperty ( p ) && typeof o [ p ] === 'object' ) {
184
+ result = findById ( o [ p ] , key ) ;
185
+ if ( result ) {
186
+ return result ;
187
+ }
188
+ }
189
+ }
190
+ return result ;
191
+ }
192
+
193
+
92
194
/**
93
195
* Get a dictionary of empty arrays for each ExportHeader field
94
196
* FUTURE: enable it to work with hierarchic vocabulary lists
@@ -112,9 +214,9 @@ const getRowMap = (sourceRow, sourceFields, RuleDB, fields, fieldNameMap, prefix
112
214
// has a mapping for export to a GRDI target field above, then set target
113
215
// to value.
114
216
if ( value && value . length > 0 ) {
115
- const vocabulary = fields [ sourceIndex ] . vocabulary ;
116
- if ( value in vocabulary ) {
117
- const term = vocabulary [ value ] ;
217
+ const vocab_list = fields [ sourceIndex ] [ 'schema:ItemList' ] ;
218
+ if ( value in vocab_list ) {
219
+ const term = vocab_list [ value ] ;
118
220
// Looking for term.exportField['GRDI'] for example:
119
221
if ( 'exportField' in term && prefix in term . exportField ) {
120
222
for ( let mapping of term . exportField [ prefix ] ) {
0 commit comments