Skip to content

Commit 22f56db

Browse files
authored
Merge pull request #10 from OpenConceptLab/feature/generalize-bridge-matching
OpenConceptLab/ocl_issues#2471 | Generalize bridge matching
2 parents 2dc637d + c996514 commit 22f56db

5 files changed

Lines changed: 74 additions & 28 deletions

File tree

src/components/map-projects/Candidates.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ const Candidates = ({rowIndex, alert, setAlert, candidates, setShowItem, showIte
612612
bucketId={`${rowIndex}-${algo.id}`}
613613
noToolbar={i !== 0}
614614
isFirst={i === 0}
615-
bridge={algo.id === 'ocl-ciel-bridge'}
615+
bridge={algo.type?.includes('bridge')}
616616
scispacy={algo.id === 'ocl-scispacy-loinc'}
617617
collapsed={collapsed}
618618
onCollapse={setCollapsed}

src/components/map-projects/Concept.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ const ConceptItem = ({_id, notClickable, isSelectedToShow, firstChild, lastChild
184184

185185

186186
const Concept = ({_id, firstChild, lastChild, concept, setShowHighlights, isShown, onCardClick, onMap, isSelectedForMap, noScore, repoVersion, isAIRecommended, AIRecommendedCandidateId, sx, notClickable, noSynonymPrefix, locales, showAlgo, candidatesScore, algoScoreFirst, asTarget, conceptCache}) => {
187-
const bridge = concept?.search_meta?.algorithm === 'ocl-ciel-bridge'
187+
const bridge = concept?.search_meta?.algorithm?.includes('bridge')
188188
const scispacy = concept?.search_meta?.algorithm === 'ocl-scispacy-loinc'
189189
const id = concept?.version_url || concept?.url || concept?.id
190190
const isSelectedToShow = isShown ? isShown(id) : false

src/components/map-projects/ConfigurationForm.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ const VisuallyHiddenInput = styled('input')({
4646
});
4747

4848

49-
const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, name, setName, description, setDescription, repo, onRepoChange, repoVersion, setRepoVersion, versions, mappedSources, targetSourcesFromRows, algosSelected, setAlgosSelected, sx, algos, validColumns, columns, isValidColumnValue, updateColumn, configure, setConfigure, columnVisibilityModel, setColumnVisibilityModel, onSave, isSaving, candidatesScore, onScoreChange, includeDefaultFilter, setIncludeDefaultFilter, filters, setFilters, locales, isLoadingLocales, setAIAssistantColumns, AIAssistantColumns, inAIAssistantGroup, lookupConfig, setLookupConfig, canBridge, canScispacy }) => {
49+
const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, name, setName, description, setDescription, repo, onRepoChange, repoVersion, setRepoVersion, versions, mappedSources, targetSourcesFromRows, algosSelected, setAlgosSelected, sx, algos, validColumns, columns, isValidColumnValue, updateColumn, configure, setConfigure, columnVisibilityModel, setColumnVisibilityModel, onSave, isSaving, candidatesScore, onScoreChange, includeDefaultFilter, setIncludeDefaultFilter, filters, setFilters, locales, isLoadingLocales, setAIAssistantColumns, AIAssistantColumns, inAIAssistantGroup, lookupConfig, setLookupConfig, canBridge, isCoreUser, canScispacy }) => {
5050
const { t } = useTranslation();
5151
const isLLMAlgoNotAllowed = !repoVersion?.match_algorithms?.includes('llm')
5252
const appliedLocales = filters?.locale ? filters?.locale?.split(',') : []
5353
const getAlgos = () => {
5454
return algos.map(algo => {
5555
if(algo.type === 'ocl-semantic')
5656
algo.disabled = Boolean(isLLMAlgoNotAllowed)
57-
else if(algo.type === 'ocl-ciel-bridge')
57+
else if(['ocl-bridge', 'ocl-ciel-bridge'].includes(algo.type))
5858
algo.disabled = !canBridge
5959
else if(algo.type === 'ocl-scispacy')
6060
algo.disabled = !canScispacy
@@ -221,6 +221,7 @@ const ConfigurationForm = ({ project, handleFileUpload, file, owner, setOwner, n
221221
value={algosSelected}
222222
onChange={setAlgosSelected}
223223
repo={repoVersion}
224+
isCoreUser={isCoreUser}
224225
/>
225226
<>
226227
<Typography component="div" sx={{fontSize: '16px', fontWeight: 'bold', marginTop: '20px'}}>

src/components/map-projects/MapProject.jsx

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ const MapProject = () => {
195195
const [repo, setRepo] = React.useState(false)
196196
const [repoVersion, setRepoVersion] = React.useState(false)
197197
const [mappedSources, setMappedSources] = React.useState([])
198-
const [CIELMappedSources, setCIELMappedSources] = React.useState([])
198+
const [bridgeMappedSources, setBridgeMappedSources] = React.useState({})
199199
const [locales, setLocales] = React.useState([])
200200
const [isLoadingLocales, setIsLoadingLocales] = React.useState(false)
201201
const [versions, setVersions] = React.useState([])
@@ -239,7 +239,8 @@ const MapProject = () => {
239239
const canScispacy = Boolean(canBridge && SCISPACY_API_URL && toggles.SCISPACY_LOINC_TOGGLE === true)
240240
const isMultiAlgo = algosSelected.length > 1
241241
const scispacyEnabled = find(algosSelected, {type: 'ocl-scispacy'})
242-
const bridgeEnabled = find(algosSelected, {type: 'ocl-ciel-bridge'})
242+
const bridgeAlgo = find(algosSelected, a => ['ocl-bridge', 'ocl-ciel-bridge'].includes(a.type))
243+
const bridgeEnabled = Boolean(bridgeAlgo)
243244

244245
const baseAlgos = useAlgos(t, toggles)
245246
const [apiAlgos, setApiAlgos] = React.useState([]);
@@ -257,8 +258,12 @@ const MapProject = () => {
257258
const response = await service.get();
258259
const _algos = response?.data?.results || []
259260
setApiAlgos(_algos);
260-
if(find(_algos, {type: 'ocl-ciel-bridge'}))
261-
fetchMappedSources('/orgs/CIEL/sources/CIEL/latest/', setCIELMappedSources)
261+
const bridgeAlgoFromApi = find(_algos, a => ['ocl-bridge', 'ocl-ciel-bridge'].includes(a.type))
262+
if(bridgeAlgoFromApi) {
263+
const bridgeUrl = bridgeAlgoFromApi.target_repo_url || '/orgs/CIEL/sources/CIEL/'
264+
fetchMappedSources(bridgeUrl + 'latest/', sources =>
265+
setBridgeMappedSources(prev => ({...prev, [bridgeUrl]: sources})))
266+
}
262267
} catch {
263268
// pass
264269
}
@@ -1224,7 +1229,7 @@ const MapProject = () => {
12241229
forEach(_selectedAlgos, algo => {
12251230
if(['custom', 'ocl-search', 'ocl-semantic'].includes(algo.type))
12261231
algoPromises.push(processWithConcurrency(repo, algo, rowsToProcess));
1227-
else if(algo.type === 'ocl-ciel-bridge' && canBridge)
1232+
else if(['ocl-bridge', 'ocl-ciel-bridge'].includes(algo.type) && canBridge)
12281233
algoPromises.push(fetchBulkBridgeCandidates(rowsToProcess, algo))
12291234
else if(algo.type === 'ocl-scispacy' && canScispacy)
12301235
algoPromises.push(fetchBulkScispacyCandidates(rowsToProcess, algo))
@@ -1485,7 +1490,7 @@ const MapProject = () => {
14851490
const getBulkBridgeCandidatesButtonLabel = () => {
14861491
const effectiveEnd = loadingMatches ? _now : bridgeCandidatesEndedAt;
14871492
const matchingDuration = getMatchingDuration(bridgeCandidatesStartedAt, effectiveEnd)
1488-
if(loadingMatches || allCandidatesRef.current['ocl-ciel-bridge']?.length)
1493+
if(loadingMatches || Object.keys(allCandidatesRef.current).some(k => k.includes('bridge') && allCandidatesRef.current[k]?.length))
14891494
return `${t('map_project.bridge_candidates')} (${matchingDuration})`
14901495
return t('map_project.bridge_candidates')
14911496
}
@@ -1704,7 +1709,7 @@ const MapProject = () => {
17041709
__candidates = times(CANDIDATES_LIMIT, i => __candidates[i])
17051710
forEach(__candidates, (candidate, i) => {
17061711
if(candidate?.id) {
1707-
const isBridge = algoId === 'ocl-ciel-bridge'
1712+
const isBridge = algoId.includes('bridge')
17081713
candidates[`__result_${algoKey}_${twoDigit(i + 1)}__`] = candidate?.id ?
17091714
(
17101715
isBridge ?
@@ -2135,7 +2140,7 @@ const MapProject = () => {
21352140
fetchOCLOrCustomCandidates(algoDef, _row, offset, _retired, _filters, onResponse)
21362141
} else if (algoDef.type === 'ocl-scispacy') {
21372142
fetchScispacyCandidates(__row, scrollToBottom, forceReload, false, onResponse)
2138-
} else if (algoDef.type === 'ocl-ciel-bridge') {
2143+
} else if (['ocl-bridge', 'ocl-ciel-bridge'].includes(algoDef.type)) {
21392144
fetchBridgeCandidates(__row, offset, _retired, scrollToBottom, _filters, forceReload, false, onResponse)
21402145
}
21412146
} else {
@@ -2265,7 +2270,8 @@ const MapProject = () => {
22652270

22662271
const fetchBridgeCandidates = (_row, offset=0, _retired, scrollToBottom, _filters, forceReload=false, isBulk=false, callback) => {
22672272
let __row = isEmpty(_row) ? row : _row
2268-
const existingCandidates = find(allCandidatesRef.current['ocl-ciel-bridge'], c => c.row.__index === __row.__index)?.results
2273+
const bridgeAlgoId = bridgeAlgo?.id || 'ocl-ciel-bridge'
2274+
const existingCandidates = find(allCandidatesRef.current[bridgeAlgoId], c => c.row.__index === __row.__index)?.results
22692275
if(!isBulk && !forceReload && offset === 0 && !_retired && existingCandidates?.length> 0) {
22702276
setTimeout(() => highlightTexts(existingCandidates, null, false), 100)
22712277
return
@@ -2284,8 +2290,8 @@ const MapProject = () => {
22842290
callback(candidates, payload)
22852291
},
22862292
(response, errorMsg) => {
2287-
markAlgo(__row.__index, 'ocl-ciel-bridge', -2)
2288-
log({action: 'algo_failed', extras: {algo: 'ocl-ciel-bridge'}}, __row.__index)
2293+
markAlgo(__row.__index, bridgeAlgoId, -2)
2294+
log({action: 'algo_failed', extras: {algo: bridgeAlgoId}}, __row.__index)
22892295
setAlert({message: response?.detail || errorMsg, severity: 'error'})
22902296
setIsLoadingInDecisionView(false)
22912297
}
@@ -2296,7 +2302,7 @@ const MapProject = () => {
22962302
const algo = algoId ? getAlgoDef(algoId) : null
22972303
if(algo?.lookup_required && (lookupConfig?.url || repoVersion.url) && candidates && isArray(candidates) && candidates.length) {
22982304
candidates.forEach(concept => {
2299-
if(algo.type === 'ocl-ciel-bridge') {
2305+
if(['ocl-bridge', 'ocl-ciel-bridge'].includes(algo.type)) {
23002306
forEach(concept.mappings, mapping => {
23012307
lookupCode(mapping.cascade_target_concept_code)
23022308
})
@@ -2624,6 +2630,7 @@ const MapProject = () => {
26242630
isLoadingLocales={isLoadingLocales}
26252631
bridgeEnabled={bridgeEnabled}
26262632
canBridge={canBridge}
2633+
isCoreUser={isCoreUser}
26272634
canScispacy={canScispacy}
26282635
scispacyEnabled={scispacyEnabled}
26292636
setAIAssistantColumns={setAIAssistantColumns}
@@ -2637,16 +2644,20 @@ const MapProject = () => {
26372644
return permissionDenied ? <Error403/> : (
26382645
<div className='col-xs-12 padding-0' style={{borderRadius: '10px', width: 'calc(100vw - 32px)'}}>
26392646
{
2640-
Boolean(repoVersion?.url) && CIELMappedSources.length > 0 &&
2641-
<BridgeMatch
2642-
service={getMatchAPIService()}
2643-
repo={repoVersion}
2644-
bridgeRepoURL='/orgs/CIEL/sources/CIEL/'
2645-
limit={CANDIDATES_LIMIT}
2646-
user={user}
2647-
ref={bridgeRef}
2648-
mappedRepoURLs={CIELMappedSources.map(source => source.url)}
2649-
/>
2647+
(() => {
2648+
const bridgeUrl = bridgeAlgo?.target_repo_url || '/orgs/CIEL/sources/CIEL/'
2649+
const mappedSrcs = bridgeMappedSources[bridgeUrl] || []
2650+
return Boolean(repoVersion?.url) && mappedSrcs.length > 0 &&
2651+
<BridgeMatch
2652+
service={getMatchAPIService()}
2653+
repo={repoVersion}
2654+
bridgeRepoURL={bridgeUrl}
2655+
limit={CANDIDATES_LIMIT}
2656+
user={user}
2657+
ref={bridgeRef}
2658+
mappedRepoURLs={mappedSrcs.map(source => source.url)}
2659+
/>
2660+
})()
26502661
}
26512662
{
26522663
loadingProject &&

src/components/map-projects/MultiAlgoSelector.jsx

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ export default function MultiAlgoSelector({
7474
value,
7575
onChange,
7676
maxAlgos=5,
77-
repo
77+
repo,
78+
isCoreUser
7879
}) {
7980
const { t } = useTranslation()
8081
const [expanded, setExpanded] = useState(() => new Map());
@@ -213,7 +214,7 @@ export default function MultiAlgoSelector({
213214
return <MatchingIcon sx={{fontSize: '1.5rem', color: 'primary.main'}} />
214215
if(algo.type === 'ocl-semantic' && algo.provider === 'ocl')
215216
return <MatchingIcon sx={{fontSize: '1.5rem', color: 'primary.main'}} />
216-
if(algo.type === 'ocl-ciel-bridge' && algo.provider === 'ocl')
217+
if(algo.type?.includes('bridge') && algo.provider === 'ocl')
217218
return <i className="fa-solid fa-bridge" style={{fontSize: '1.5rem', color: 'primary.main'}} />
218219
if(algo.type === 'ocl-scispacy' && algo.provider === 'ocl')
219220
return <img src="https://allenai.github.io/scispacy/scispacy-logo-square.png" style={{objectFit: 'cover', width: '28px', height: '28px'}} />
@@ -503,6 +504,39 @@ export default function MultiAlgoSelector({
503504
</Stack>
504505
</Stack>
505506
</Paper>
507+
) : algo.type?.includes('bridge') ? (
508+
<Stack spacing={1.5}>
509+
{isCoreUser && (
510+
<TextField
511+
fullWidth
512+
label={t('map_project.bridge_source_url') || 'Bridge Source URL'}
513+
value={sel.target_repo_url ?? algo.target_repo_url ?? '/orgs/CIEL/sources/CIEL/'}
514+
onChange={(e) => updateSelected(sel.__key, { target_repo_url: e.target.value })}
515+
placeholder="/orgs/CIEL/sources/CIEL/"
516+
helperText={t('map_project.bridge_source_url_description') || 'The interface terminology to search through for bridge matching'}
517+
/>
518+
)}
519+
<Stack direction={{ xs: "column", sm: "row" }} spacing={1.5}>
520+
<TextField
521+
label={t('map_project.batch_size')}
522+
sx={{width: '50%'}}
523+
type="number"
524+
value={sel.batch_size ?? algo.batch_size ?? 10}
525+
onChange={(e) => updateSelected(sel.__key, { batch_size: clampInt(e.target.value, 1, 1000) })}
526+
/>
527+
<TextField
528+
label={t('map_project.concurrent_requests')}
529+
sx={{width: '50%'}}
530+
type="number"
531+
value={sel.concurrent_requests ?? algo.concurrent_requests ?? 1}
532+
onChange={(e) =>
533+
updateSelected(sel.__key, {
534+
concurrent_requests: clampInt(e.target.value, 1, 50),
535+
})
536+
}
537+
/>
538+
</Stack>
539+
</Stack>
506540
) : (
507541
<Stack direction={{ xs: "column", sm: "row" }} spacing={1.5}>
508542
<TextField

0 commit comments

Comments
 (0)