Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 41 additions & 12 deletions src/components/map-projects/AutoMatchDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ import AIAssistantSelectorPanel from './AIAssistantSelectorPanel'
const AutoMatchDialog = ({
open,
onClose,
autoMatchUnmappedOnly,
setAutoMatchUnmappedOnly,
autoMatchScope,
setAutoMatchScope,
rowStatuses,
selectedRowCount,
autoRunAIAnalysis,
setAutoRunAIAnalysis,
AIModels,
Expand All @@ -47,11 +48,31 @@ const AutoMatchDialog = ({
}) => {
const { t } = useTranslation()
const [algos, setAlgos] = React.useState(true)
const selectedRows = autoMatchUnmappedOnly ? rowStatuses.unmapped.length : (rowStatuses.unmapped.length + rowStatuses.readyForReview.length)
const allRowsCount = rowStatuses.unmapped.length + rowStatuses.readyForReview.length
const rowsInSelectedScope = {
unmapped: rowStatuses.unmapped.length,
all: allRowsCount,
selected: selectedRowCount
}
const rowsToMatchCount = rowsInSelectedScope[autoMatchScope] || 0
const totalRows = rowStatuses.unmapped.length + rowStatuses.readyForReview.length + rowStatuses.reviewed.length
const hasSelectedRows = selectedRowCount > 0
const hasUnmappedRows = rowStatuses.unmapped.length > 0

React.useEffect(() => {
if (autoMatchScope === 'unmapped' && !hasUnmappedRows) {
setAutoMatchScope('all')
}
}, [autoMatchScope, hasUnmappedRows, setAutoMatchScope])

const getHelperTextForAutoMatchUnmapped = () => {
if (autoMatchUnmappedOnly) {
if (autoMatchScope === 'selected') {
if (hasSelectedRows) {
return t('map_project.auto_match_selected_rows_note', {count: selectedRowCount.toLocaleString()});
}
return '';
}
if (autoMatchScope === 'unmapped') {
const count = rowStatuses.unmapped.length;
if (count > 0) {
return t('map_project.auto_match_unmapped_only_note', {count: count.toLocaleString()});
Expand All @@ -69,7 +90,7 @@ const AutoMatchDialog = ({
return t('map_project.auto_match_note_no_counts');
};

const isDisabled = !repoVersion?.version_url || selectedRows === 0 || (!algos && !autoRunAIAnalysis)
const isDisabled = !repoVersion?.version_url || rowsToMatchCount === 0 || (!algos && !autoRunAIAnalysis)

return (
<Dialog
Expand Down Expand Up @@ -98,22 +119,30 @@ const AutoMatchDialog = ({
}
</div>
<FormControl sx={{marginTop: '16px'}}>
<FormLabel id="automatch-rows">{`${t('map_project.selected_rows')}: ${selectedRows.toLocaleString()} ${t('map_project.out_of')} ${totalRows.toLocaleString()}` }</FormLabel>
<FormLabel id="automatch-rows">{`${t('map_project.rows_to_match')}: ${rowsToMatchCount.toLocaleString()} ${t('map_project.out_of')} ${totalRows.toLocaleString()}` }</FormLabel>
<RadioGroup
row
aria-labelledby="automatch-rows"
name="automatch-rows"
onChange={() => setAutoMatchUnmappedOnly(!autoMatchUnmappedOnly)}
value={autoMatchScope}
onChange={event => setAutoMatchScope(event.target.value)}
>
<FormControlLabel
value="all"
control={<Radio />}
label={`${t('map_project.all_rows')} (${allRowsCount.toLocaleString()})`}
/>
<FormControlLabel
value="unmapped"
control={<Radio checked={autoMatchUnmappedOnly} />}
label={t('map_project.unmapped_only')}
disabled={!hasUnmappedRows}
control={<Radio />}
label={`${t('map_project.unmapped_only')} (${rowStatuses.unmapped.length.toLocaleString()})`}
/>
<FormControlLabel
value="all"
control={<Radio checked={!autoMatchUnmappedOnly} />}
label={t('map_project.all_rows')}
value="selected"
disabled={!hasSelectedRows}
control={<Radio />}
label={`${t('map_project.selected_rows')} (${selectedRowCount.toLocaleString()})`}
/>
</RadioGroup>
</FormControl>
Expand Down
54 changes: 43 additions & 11 deletions src/components/map-projects/MapProject.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ const MapProject = () => {

const [matchDialog, setMatchDialog] = React.useState(false)
const [showItem, setShowItem] = React.useState(false)
const [autoMatchUnmappedOnly, setAutoMatchUnmappedOnly] = React.useState(true)
const [autoMatchScope, setAutoMatchScope] = React.useState('unmapped')
const [autoRunAIAnalysis, setAutoRunAIAnalysis] = React.useState(false)
const [alert, setAlert] = React.useState(false)
const [columnVisibilityModel, setColumnVisibilityModel] = React.useState({})
Expand Down Expand Up @@ -1008,7 +1008,7 @@ const MapProject = () => {
setDecisionTab('candidates')
setSearchText('')
setShowItem(false)
setAutoMatchUnmappedOnly(true)
setAutoMatchScope('unmapped')
setAlert(false)
setSelectedCandidatesScoreBucket(false)
setScoreBucketSortBy('desc')
Expand Down Expand Up @@ -1591,6 +1591,16 @@ const MapProject = () => {

const getRowsResults = async (rows, selectedAlgos) => {
abortRef.current = false;
const selectedRowIndexes = getSelectedRowIndexes(rows)
const selectedRowIndexSet = new Set(selectedRowIndexes)
const isAutoMatchUnmappedOnly = autoMatchScope === 'unmapped'
const isAutoMatchAllRows = autoMatchScope === 'all'
const isAutoMatchSelectedRows = autoMatchScope === 'selected'
const selectedRowsLogExtras = isAutoMatchSelectedRows ? {
selected_rows_count: selectedRowIndexes.length,
row_indexes: selectedRowIndexes,
selected_row_indexes: selectedRowIndexes
} : {}

// Function to process a single batch
const processBatch = async (_repo, rowBatch, algo) => {
Expand Down Expand Up @@ -1729,15 +1739,18 @@ const MapProject = () => {
let _selectedAlgos = filter(algosSelected, algo => selectedAlgos.includes(algo.id))
let subActions = [...map(_selectedAlgos, algo => algo.name || algo.id)]
subActions.push('reranker')
if(autoMatchUnmappedOnly)
if(isAutoMatchUnmappedOnly)
subActions.push('unmatched_only')
if(isAutoMatchSelectedRows)
subActions.push('selected_rows')
if(inAIAssistantGroup && autoRunAIAnalysis)
subActions.push('with_ai_analysis')

projectLog({
action: 'auto_match_started',
extras: {
sub_actions: subActions,
...selectedRowsLogExtras,
...(inAIAssistantGroup && autoRunAIAnalysis ? {
ai_assistant: {
model: getSelectedAIModel(),
Expand All @@ -1747,13 +1760,25 @@ const MapProject = () => {
}
})

if(!autoMatchUnmappedOnly)
if(isAutoMatchAllRows)
setRowStatuses(prev => ({...prev, readyForReview: []}))
if(isAutoMatchSelectedRows)
setRowStatuses(prev => ({
...prev,
readyForReview: without(prev.readyForReview, ...selectedRowIndexes),
reviewed: without(prev.reviewed, ...selectedRowIndexes)
}))

setTimeout(async () => {
const rowsToProcess = autoMatchUnmappedOnly
const rowsToProcess = isAutoMatchUnmappedOnly
? filter(rows, row => rowStatuses.unmapped.includes(row.__index))
: filter(rows, row => !rowStatuses.reviewed.includes(row.__index))
: filter(rows, row => {
if(isAutoMatchSelectedRows)
return selectedRowIndexSet.has(row.__index)
if(rowStatuses.reviewed.includes(row.__index))
return false
return true
})

// ocl_online#105 Phase 5: open the run record, then guarantee it is
// closed out (completed / partial / failed / cancelled) via the finally,
Expand Down Expand Up @@ -1805,6 +1830,7 @@ const MapProject = () => {
action: 'auto_match_finished',
extras: {
sub_actions: subActions,
...selectedRowsLogExtras,
...(inAIAssistantGroup && autoRunAIAnalysis ? {
ai_assistant: {
model: getSelectedAIModel(),
Expand Down Expand Up @@ -2043,6 +2069,7 @@ const MapProject = () => {
const onGetCandidates = event => {
event.stopPropagation()
event.preventDefault()
setAutoMatchScope(getSelectedRowIndexes().length ? 'selected' : (rowStatuses.unmapped.length ? 'unmapped' : 'all'))
setMatchDialog(true)
}

Expand Down Expand Up @@ -2677,7 +2704,10 @@ const MapProject = () => {
})
}

const getSelectedRowIndexes = () => selectedRowIds.map(id => parseInt(id)).filter(Number.isFinite)
const getSelectedRowIndexes = (_rows = data) => {
const selectedIds = new Set(selectedRowIds.map(id => id?.toString()))
return _rows.filter(_row => selectedIds.has(_row.__index?.toString())).map(_row => _row.__index)
}

const openBulkConfirm = action => {
const indexes = getSelectedRowIndexes()
Expand Down Expand Up @@ -3934,10 +3964,11 @@ const MapProject = () => {
const rows = getRows()
const visibleRowIds = rows.map(_row => _row.__index)
const visibleRowIdKey = visibleRowIds.join(',')
const selectedRowsCount = selectedRowIds.length
const selectedRowsCount = getSelectedRowIndexes(rows).length
React.useEffect(() => {
setSelectedRowIds(prev => {
const next = prev.filter(id => visibleRowIds.includes(parseInt(id)))
const visibleRowIdSet = new Set(visibleRowIds.map(id => id?.toString()))
const next = prev.filter(id => visibleRowIdSet.has(id?.toString()))
return next.length === prev.length ? prev : next
})
}, [visibleRowIdKey])
Expand Down Expand Up @@ -4965,8 +4996,9 @@ const MapProject = () => {
onSubmit={onGetCandidatesSubmit}
{...{
rowStatuses,
autoMatchUnmappedOnly,
setAutoMatchUnmappedOnly,
autoMatchScope,
setAutoMatchScope,
selectedRowCount: selectedRowsCount,
autoRunAIAnalysis,
setAutoRunAIAnalysis,
AIModels,
Expand Down
7 changes: 5 additions & 2 deletions src/components/map-projects/ProjectLogs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,15 @@ const ProjectLogs = ({onClose, logs, project}) => {
</a>
</span>
if(['auto_match_started', 'auto_match_finished', 'auto_matched'].includes(log.action)) {
const subActions = map(log.extras?.sub_actions || [], formatSubAction).filter(subAction => log.extras?.selected_rows_count ? subAction !== 'Selected Rows' : true)
if(log.extras?.selected_rows_count)
subActions.push(`${log.extras.selected_rows_count.toLocaleString()} Selected Rows`)
return <span>
{startCase(log.action)}
{
log.extras?.sub_actions?.length ?
subActions.length ?
<span style={{marginLeft: '4px'}}>
{`(${map(log.extras.sub_actions, formatSubAction).join(', ')})`}
{`(${subActions.join(', ')})`}
</span> :
null
}
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,8 @@
"auto_match_unmapped_only_note_no_count": "Note: Skip input rows that are already proposed",
"auto_match_note": "Note: This will not affect {{approvedCount}} approved input rows but will override {{proposedCount}} proposed input rows",
"auto_match_note_no_counts": "Note: This will not affect approved input rows but will override proposed input rows",
"auto_match_selected_rows_note": "Note: This will run Auto Match for {{count}} selected input rows",
"auto_match_selected_rows_note_no_count": "Note: Select rows in the left panel to enable this option",
"run_ai_analysis": "Run AI Analysis",
"run_ai_analysis_note": "Note: Enabling this feature will run AI Analysis on results of each row. This has direct cost implications.",
"decision": "Decision",
Expand Down Expand Up @@ -632,6 +634,7 @@
"select_an_algo": "Select an algorithm",
"selected_rows": "Selected Rows",
"all_rows": "All Rows",
"rows_to_match": "Rows to Match",
"out_of": "out of",
"retrieve_candidates": "Retrieve Candidates",
"retrieve_candidates_helper_text": "Your project is configured to use the following match algorithms:",
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/es/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,8 @@
"auto_match_unmapped_only_note_no_count": "Nota: Omitir filas de entrada que ya están propuestas",
"auto_match_note": "Nota: Esto no afectará {{approvedCount}} filas de entrada aprobadas pero sobrescribirá {{proposedCount}} filas de entrada propuestas",
"auto_match_note_no_counts": "Nota: Esto no afectará las filas de entrada aprobadas pero sobrescribirá las filas de entrada propuestas",
"auto_match_selected_rows_note": "Nota: Esto ejecutará Auto Match para {{count}} fila(s) seleccionada(s)",
"auto_match_selected_rows_note_no_count": "Nota: Seleccione filas en el panel izquierdo para habilitar esta opción",
"run_ai_analysis": "Ejecutar Análisis de IA",
"run_ai_analysis_note": "Nota: Habilitar esta función ejecutará Análisis de IA en los resultados de cada fila. Esto tiene implicaciones de costo directas.",
"decision": "Decisión",
Expand Down Expand Up @@ -609,6 +611,7 @@
"select_an_algo": "Seleccione un algoritmo",
"selected_rows": "Filas seleccionadas",
"all_rows": "Todas las filas",
"rows_to_match": "Filas para coincidir",
"out_of": "de",
"retrieve_candidates": "Recuperar candidatos",
"retrieve_candidates_helper_text": "Su proyecto está configurado para usar los siguientes algoritmos de coincidencia:",
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/zh/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@
"auto_match_unmapped_only_note_no_count": "注意:跳过已提议的输入行",
"auto_match_note": "注意:这不会影响 {{approvedCount}} 个已批准的输入行,但会覆盖 {{proposedCount}} 个已提议的输入行",
"auto_match_note_no_counts": "注意:这不会影响已批准的输入行,但会覆盖已提议的输入行",
"auto_match_selected_rows_note": "注意:这将对 {{count}} 个已选输入行运行自动匹配",
"auto_match_selected_rows_note_no_count": "注意:请在左侧面板选择行以启用此选项",
"run_ai_analysis": "运行 AI 分析",
"run_ai_analysis_note": "注意:启用此功能将对每行的结果运行 AI 分析。这会产生直接的成本影响。",
"decision": "决策",
Expand Down Expand Up @@ -634,6 +636,7 @@
"select_an_algo": "选择算法",
"selected_rows": "已选行",
"all_rows": "所有行",
"rows_to_match": "要匹配的行",
"out_of": "共",
"retrieve_candidates": "获取候选项",
"retrieve_candidates_helper_text": "你的项目已配置为使用以下匹配算法:",
Expand Down