Skip to content

Commit 43c9896

Browse files
committed
OpenConceptLab/ocl_issues#2183 | Map code/list configs
1 parent 7213b96 commit 43c9896

4 files changed

Lines changed: 175 additions & 54 deletions

File tree

src/components/map-projects/ColumnMapTable.jsx

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import InvalidIcon from '@mui/icons-material/CancelOutlined';
1313
import find from 'lodash/find';
1414
import map from 'lodash/map';
1515
import omit from 'lodash/omit';
16+
import get from 'lodash/get'
1617

1718

1819
const HeaderAutocomplete = ({headers, isValid, ...rest}) => {
@@ -63,10 +64,44 @@ const HeaderAutocomplete = ({headers, isValid, ...rest}) => {
6364
)
6465
}
6566

66-
const ColumnMapTable = ({validColumns, columns, isValid, onUpdate, sx, setColumnVisibilityModel, columnVisibilityModel}) => {
67+
68+
const TargetSourceAutoComplete = ({sources, possibleValue, selected, onChange, ...rest}) => {
69+
React.useEffect(() => {
70+
if(possibleValue && !selected) {
71+
const possibleSource = find(sources, source => {
72+
let matched = source.id.toLowerCase().match(possibleValue.toLowerCase())
73+
if(!matched)
74+
matched = source.id.toLowerCase().replace('-', '').replace(' ', '').replace('_', '').match(possibleValue.toLowerCase().replace('-', '').replace(' ', '').replace('_', ''))
75+
return Boolean(matched)
76+
})
77+
if(possibleSource?.url)
78+
onChange(possibleSource?.url)
79+
}
80+
}, [])
81+
return <Autocomplete
82+
autoHighlight
83+
autoComplete
84+
disablePortal
85+
blurOnSelect
86+
fullWidth
87+
getOptionLabel={option => option.id || ''}
88+
isOptionEqualToValue={(option, value) => option?.url === value}
89+
sx={{
90+
minWidth: '100px',
91+
'.MuiButtonBase-root': {color: '#000'}
92+
}}
93+
renderInput={(params) => <TextField margin='dense' size='small' {...params} />}
94+
options={sources}
95+
value={selected ? find(sources, {url: selected}) || '' : ''}
96+
onChange={(event, val) => onChange(val?.url)}
97+
{...rest}
98+
/>
99+
}
100+
101+
const ColumnMapTable = ({validColumns, columns, isValid, onUpdate, sx, setColumnVisibilityModel, columnVisibilityModel, mappedSources, targetSourcesFromRows}) => {
67102
const [random, setRandom] = React.useState(0)
68-
const onValChange = (position, value) => {
69-
onUpdate(position, value)
103+
const onValChange = (position, value, key) => {
104+
onUpdate(position, value, key)
70105
setRandom(random + 1)
71106
}
72107
return (
@@ -96,13 +131,51 @@ const ColumnMapTable = ({validColumns, columns, isValid, onUpdate, sx, setColumn
96131
</span>
97132
</TableCell>
98133
<TableCell>
134+
<>
99135
<HeaderAutocomplete
100136
headers={validColumns}
101137
value={column.label}
102138
id={position.toString()}
103139
isValid={isValidColumn}
104140
onChange={(event, value) => onValChange(position, value?.label || value)}
105141
/>
142+
{
143+
'Mapping: List' === column.label &&
144+
<div>
145+
{
146+
map(targetSourcesFromRows[column.dataKey], (target, index) => {
147+
return (
148+
<div key={index} style={{display: 'flex', alignItems: 'center'}}>
149+
<span style={{marginRight: '8px', textTransform: 'uppercase'}}>
150+
{target}:
151+
</span>
152+
<TargetSourceAutoComplete
153+
sources={mappedSources}
154+
possibleValue={target}
155+
selected={get(column.targetSource, target)}
156+
onChange={value => onValChange(position, {...column.targetSource, [target]: value}, 'targetSource')}
157+
/>
158+
</div>
159+
)
160+
})
161+
}
162+
</div>
163+
}
164+
{
165+
'Mapping: Code' === column.label &&
166+
<div style={{display: 'flex', alignItems: 'center'}}>
167+
<span style={{marginRight: '8px', textTransform: 'uppercase'}}>
168+
{targetSourcesFromRows[column.dataKey]}:
169+
</span>
170+
<TargetSourceAutoComplete
171+
sources={mappedSources}
172+
possibleValue={targetSourcesFromRows[column.dataKey]}
173+
selected={get(column.targetSource, targetSourcesFromRows[column.dataKey])}
174+
onChange={value => onValChange(position, {...column.targetSource, [targetSourcesFromRows[column.dataKey]]: value}, 'targetSource')}
175+
/>
176+
</div>
177+
}
178+
</>
106179
</TableCell>
107180
<TableCell>
108181
<Switch size="small" checked={!isHidden} onChange={() => setColumnVisibilityModel(isHidden ? omit(columnVisibilityModel, [column.dataKey]) : {...columnVisibilityModel, [column.dataKey]: false})}/>

src/components/map-projects/ConfigurationForm.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const VisuallyHiddenInput = styled('input')({
3636
});
3737

3838

39-
const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, name, setName, description, setDescription, repo, onRepoChange, repoVersion, setRepoVersion, versions, algo, onAlgoSelect, sx, algos, validColumns, columns, isValidColumnValue, updateColumn, configure, setConfigure, columnVisibilityModel, setColumnVisibilityModel, onSave }) => {
39+
const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, name, setName, description, setDescription, repo, onRepoChange, repoVersion, setRepoVersion, versions, mappedSources, targetSourcesFromRows, algo, onAlgoSelect, sx, algos, validColumns, columns, isValidColumnValue, updateColumn, configure, setConfigure, columnVisibilityModel, setColumnVisibilityModel, onSave }) => {
4040
const [algoMenuAnchorEl, setAlgoMenuAnchorEl] = React.useState(null)
4141

4242
const onAlgoButtonClick = event => setAlgoMenuAnchorEl(algoMenuAnchorEl ? null : event.currentTarget)
@@ -183,6 +183,8 @@ const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, n
183183
onUpdate={updateColumn}
184184
columnVisibilityModel={columnVisibilityModel}
185185
setColumnVisibilityModel={setColumnVisibilityModel}
186+
mappedSources={mappedSources}
187+
targetSourcesFromRows={targetSourcesFromRows}
186188
/>
187189
</>
188190
}

src/components/map-projects/MapProject.jsx

Lines changed: 94 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ import sum from 'lodash/sum'
5555
import omit from 'lodash/omit'
5656
import reject from 'lodash/reject'
5757
import uniq from 'lodash/uniq'
58+
import compact from 'lodash/compact'
59+
import flatten from 'lodash/flatten'
5860
import debounce from 'lodash/debounce'
5961
import keys from 'lodash/keys'
6062
import pickBy from 'lodash/pickBy'
@@ -151,12 +153,16 @@ const MapProject = () => {
151153
// repo state
152154
const [repo, setRepo] = React.useState(false)
153155
const [repoVersion, setRepoVersion] = React.useState(false)
156+
const [mappedSources, setMappedSources] = React.useState([])
154157
const [versions, setVersions] = React.useState([])
155158
const [conceptCache, setConceptCache] = React.useState({})
156159
const [allMapTypes, setAllMapTypes] = React.useState([])
157160
const [random, setRandom] = React.useState(0)
158161
const [deleteProject, setDeleteProject] = React.useState(false)
159162

163+
164+
const [targetSourcesFromRows, setTargetSourcesFromRows] = React.useState({}) //{dataKey: [source1_original_name, source2_original_name]}
165+
160166
const headers = algo === 'llm' ? SEMANTIC_SEARCH_HEADERS : HEADERS
161167

162168
const ALGOS = [
@@ -196,11 +202,9 @@ const MapProject = () => {
196202
}
197203
setAlgo(response?.data?.matching_algorithm || algo)
198204
if (response?.data.columns?.length > 0) {
199-
setColumns(response.data.columns.map(col => {
200-
let _col = {...omit(col, ['hidden'])}
201-
_col.dataKey = _col.label || _col.dataKey
202-
return _col
203-
}))
205+
const _columns = response.data.columns.map(col => ({...omit(col, ['hidden'])}))
206+
setColumns(_columns)
207+
setTargetSourcesFromRows(getTargetSourcesFromRows(_columns, data))
204208
let colVisibility = {}
205209
let colWidth = {}
206210
response.data.columns.forEach(col => {
@@ -310,10 +314,43 @@ const MapProject = () => {
310314
return cols
311315
}
312316

313-
const updateColumn = (position, newValue) => {
317+
const getTargetSourcesFromRows = (cols, _data) => {
318+
let sources = {};
319+
let __data = _data || data
320+
filter(cols, {label: 'Mapping: List'})?.forEach(col => {
321+
let values = map(__data, row => row[col.dataKey].split(',').map(source => get(source?.trim()?.split(':'), '0')))
322+
sources[col.dataKey] = uniq(compact(flatten(values)))
323+
})
324+
filter(cols, {label: 'Mapping: Code'})?.forEach(col => {
325+
sources[col.dataKey] = col.dataKey.toLowerCase().replace('_code', '')
326+
})
327+
return sources
328+
}
329+
330+
const getMapConfigs = () => {
331+
let configs = []
332+
forEach(filter(columns, col => ['Mapping: Code', 'Mapping: List'].includes(col?.label)), col => {
333+
const isList = col.label === 'Mapping: List'
334+
let config = {
335+
type: isList ? 'mapping-list' : 'mapping-code',
336+
input_column: col.dataKey,
337+
}
338+
if(isList)
339+
config.target_urls = col.targetSource
340+
else
341+
config.target_source_url = get(values(col.targetSource), '0')
342+
if((isList && !isEmpty(config.target_urls)) || (!isList && config.target_source_url))
343+
configs.push(config)
344+
})
345+
return configs
346+
}
347+
348+
const updateColumn = (position, newValue, key) => {
314349
let cols = {...columns}
315-
cols[position].label = newValue || ''
350+
cols[position][key || 'label'] = newValue || ''
316351
setColumns(cols)
352+
if(key !== 'targetSource')
353+
setTargetSourcesFromRows(getTargetSourcesFromRows(cols))
317354
}
318355

319356
const resetState = () => {
@@ -344,7 +381,6 @@ const MapProject = () => {
344381
setAlert(false)
345382
}
346383

347-
348384
const handleFileUpload = event => {
349385
resetState()
350386
const file = event.target.files[0];
@@ -415,7 +451,8 @@ const MapProject = () => {
415451
return prev
416452
})
417453

418-
setColumns(getColumns(omit(_data[0], ['__index'])))
454+
let cols = getColumns(omit(_data[0], ['__index']))
455+
setColumns(cols)
419456
}
420457

421458
const onSave = () => {
@@ -460,6 +497,14 @@ const MapProject = () => {
460497

461498
const fetchRepo = (url, _repo) => APIService.new().overrideURL(url).get().then(response => setRepo(response.data?.id ? response.data : _repo))
462499

500+
const fetchMappedSources = url => APIService.new().overrideURL(url).appendToUrl('mapped-sources/?excludeSelf=false&brief=true').get().then(response => setMappedSources(response?.data || []))
501+
502+
const onRepoVersionChange = version => {
503+
setRepoVersion(version)
504+
if(version?.version_url)
505+
fetchMappedSources(version.version_url)
506+
}
507+
463508
const onAlgoButtonClick = event => setAlgoMenuAnchorEl(algoMenuAnchorEl ? null : event.currentTarget)
464509

465510
const onAlgoSelect = newAlgo => {
@@ -477,6 +522,7 @@ const MapProject = () => {
477522
'source_version': repoVersion?.id || _repo.version || _repo.id,
478523
'source': _repo.short_code || _repo.id
479524
},
525+
map_config: getMapConfigs()
480526
}
481527
}
482528

@@ -576,14 +622,14 @@ const MapProject = () => {
576622
setVersions(_versions)
577623
if(_selectedVersion) {
578624
const _version = find(_versions, {id: _selectedVersion})
579-
setRepoVersion(_version)
625+
onRepoVersionChange(_version)
580626
}
581627
else if(_versions?.length === 1)
582-
setRepoVersion(_versions[0])
628+
onRepoVersionChange(_versions[0])
583629
else {
584630
let releasedVersion = find(_versions, {released: true})
585631
if(releasedVersion)
586-
setRepoVersion(releasedVersion)
632+
onRepoVersionChange(releasedVersion)
587633
}
588634
})
589635
}
@@ -595,6 +641,7 @@ const MapProject = () => {
595641
} else {
596642
setVersions([])
597643
setRepoVersion(false)
644+
setMappedSources([])
598645
}
599646
}
600647

@@ -609,13 +656,17 @@ const MapProject = () => {
609656
let newValue = value
610657
let newKey = key === '__index' ? key : snakeCase(key.toLowerCase())
611658
let isList = key === '__index' ? false : newValue.includes('\n')
659+
if(['Mapping: Code', 'Mapping: List'].includes(column?.label))
660+
newKey = column.dataKey
612661

613662
if(isList)
614663
newValue = newValue.split('\n')
615664
if(key.includes('__updated'))
616665
newKey = key.replace('__updated', '')
617666
if(newKey.includes('class'))
618667
newKey = 'concept_class'
668+
if(newKey.includes('datatype'))
669+
newKey = 'datatype'
619670
if(newKey === 'set_members')
620671
newKey = 'other_map_codes'
621672
if(newKey === 'same_as')
@@ -745,16 +796,8 @@ const MapProject = () => {
745796
const rowStateLabel = VIEWS[rowState].label
746797
let concept = mapSelected[index]
747798
let _repo = concept?.repo
748-
let newRow = {}
749-
forEach(row, (value, key) => {
750-
const column = find(columns, {dataKey: key})
751-
if(column?.label)
752-
newRow[column.label] = value
753-
else
754-
newRow[key] = value
755-
})
756-
newRow = {
757-
...newRow,
799+
let newRow = {
800+
...row,
758801
'__Concept ID__': concept?.id,
759802
'__Concept Name__': concept?.display_name,
760803
'__Concept URL__': concept?.url,
@@ -909,7 +952,6 @@ const MapProject = () => {
909952
updateMatchTypeCounts(null, prev)
910953
return prev
911954
})
912-
913955
}
914956

915957
const fetchOtherCandidates = (_row, offset=0) => {
@@ -1037,29 +1079,31 @@ const MapProject = () => {
10371079
file={file}
10381080
owner={owner}
10391081
setOwner={setOwner}
1040-
name={name}
1041-
setName={setName}
1042-
description={description}
1043-
setDescription={setDescription}
1044-
repo={repo}
1045-
onRepoChange={onRepoChange}
1046-
repoVersion={repoVersion}
1047-
setRepoVersion={setRepoVersion}
1048-
versions={versions}
1049-
algo={algo}
1050-
onAlgoSelect={onAlgoSelect}
1051-
algos={ALGOS}
1052-
validColumns={headers}
1053-
columns={columns}
1054-
isValidColumnValue={isValidColumnValue}
1055-
updateColumn={updateColumn}
1056-
configure={configure}
1057-
setConfigure={setConfigure}
1058-
columnVisibilityModel={columnVisibilityModel}
1059-
setColumnVisibilityModel={setColumnVisibilityModel}
1060-
onSave={onSave}
1061-
/>
1062-
</div>
1082+
name={name}
1083+
setName={setName}
1084+
description={description}
1085+
setDescription={setDescription}
1086+
repo={repo}
1087+
onRepoChange={onRepoChange}
1088+
repoVersion={repoVersion}
1089+
setRepoVersion={onRepoVersionChange}
1090+
mappedSources={mappedSources}
1091+
targetSourcesFromRows={targetSourcesFromRows}
1092+
versions={versions}
1093+
algo={algo}
1094+
onAlgoSelect={onAlgoSelect}
1095+
algos={ALGOS}
1096+
validColumns={headers}
1097+
columns={columns}
1098+
isValidColumnValue={isValidColumnValue}
1099+
updateColumn={updateColumn}
1100+
configure={configure}
1101+
setConfigure={setConfigure}
1102+
columnVisibilityModel={columnVisibilityModel}
1103+
setColumnVisibilityModel={setColumnVisibilityModel}
1104+
onSave={onSave}
1105+
/>
1106+
</div>
10631107
}
10641108
<div className='col-xs-12 padding-0' style={{backgroundColor: SURFACE_COLORS.main, marginLeft: '-5px', paddingBottom: '0px', paddingLeft: '0px', paddingTop: '0px', display: 'flex', justifyContent: 'space-between'}}>
10651109
{
@@ -1322,7 +1366,7 @@ const MapProject = () => {
13221366
{ALGOS.find(_algo => _algo.id === algo).label}
13231367
</Button>
13241368
<RepoSearchAutocomplete label='Map Target' size='small' onChange={(id, item) => onRepoChange(item)} value={repo} />
1325-
<RepoVersionSearchAutocomplete versions={versions} label='Version' size='small' onChange={(id, item) => setRepoVersion(item)} value={repoVersion} sx={{marginTop: '10px'}} />
1369+
<RepoVersionSearchAutocomplete versions={versions} label='Version' size='small' onChange={(id, item) => onRepoVersionChange(item)} value={repoVersion} sx={{marginTop: '10px'}} />
13261370
<FormControlLabel sx={{marginTop: '8px'}} control={<Checkbox checked={autoMatchUnmappedOnly} onChange={event => setAutoMatchUnmappedOnly(event.target.checked)} />} label="Unmapped Only" />
13271371
{!autoMatchUnmappedOnly && <FormHelperText sx={{marginTop: '-4px'}}>This will not affect Approved Matches but will override other existing matches</FormHelperText>}
13281372
</DialogContent>
@@ -1366,7 +1410,9 @@ const MapProject = () => {
13661410
repo={repo}
13671411
onRepoChange={onRepoChange}
13681412
repoVersion={repoVersion}
1369-
setRepoVersion={setRepoVersion}
1413+
setRepoVersion={onRepoVersionChange}
1414+
mappedSources={mappedSources}
1415+
targetSourcesFromRows={targetSourcesFromRows}
13701416
versions={versions}
13711417
algo={algo}
13721418
onAlgoSelect={onAlgoSelect}

0 commit comments

Comments
 (0)