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
18 changes: 10 additions & 8 deletions src/app/dfSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -655,19 +655,20 @@ export const dataFormulatorSlice = createSlice({
if (t.id == tableId) {
// Update metadata type inference based on new data
let newMetadata = { ...t.metadata };
const safeNewRows = newRows.filter(Boolean);
for (let name of t.names) {
if (newRows.length > 0 && name in newRows[0]) {
if (safeNewRows.length > 0 && name in safeNewRows[0]) {
newMetadata[name] = {
...newMetadata[name],
type: inferTypeFromValueArray(newRows.map(r => r[name])),
type: inferTypeFromValueArray(safeNewRows.map(r => r[name])),
};
}
}
// Update lastRefreshed timestamp if source exists
const updatedSource = t.source ? { ...t.source, lastRefreshed: Date.now() } : undefined;
// Use provided content hash (from backend for virtual/DB tables) or compute locally
// For virtual tables, backend hash reflects full table; for stream tables, compute from actual rows
const newContentHash = providedContentHash || computeContentHash(newRows, t.names);
const newContentHash = providedContentHash || computeContentHash(safeNewRows, t.names);
return { ...t, rows: newRows, metadata: newMetadata, source: updatedSource, contentHash: newContentHash };
}
Comment on lines +658 to 673
return t;
Expand All @@ -684,16 +685,17 @@ export const dataFormulatorSlice = createSlice({
const newRows = update.rows;
const providedContentHash = update.contentHash;
let newMetadata = { ...t.metadata };
const safeNewRows = newRows.filter(Boolean);
for (let name of t.names) {
if (newRows.length > 0 && name in newRows[0]) {
if (safeNewRows.length > 0 && name in safeNewRows[0]) {
newMetadata[name] = {
...newMetadata[name],
type: inferTypeFromValueArray(newRows.map(r => r[name])),
type: inferTypeFromValueArray(safeNewRows.map(r => r[name])),
};
}
}
const updatedSource = t.source ? { ...t.source, lastRefreshed: Date.now() } : undefined;
const newContentHash = providedContentHash || computeContentHash(newRows, t.names);
const newContentHash = providedContentHash || computeContentHash(safeNewRows, t.names);
return { ...t, rows: newRows, metadata: newMetadata, source: updatedSource, contentHash: newContentHash };
});
},
Expand Down Expand Up @@ -751,7 +753,7 @@ export const dataFormulatorSlice = createSlice({
}

// Create new rows with the column positioned after the first parent
let newRows = table.rows.map((row, i) => {
let newRows = table.rows.filter(Boolean).map((row, i) => {
let newRow: {[key: string]: any} = {};
for (let key of Object.keys(row)) {
newRow[key] = row[key];
Expand Down Expand Up @@ -782,7 +784,7 @@ export const dataFormulatorSlice = createSlice({
if (fieldIndex != -1) {
table.names = table.names.slice(0, fieldIndex).concat(table.names.slice(fieldIndex + 1));
delete table.metadata[fieldName];
table.rows = table.rows.map(r => {
table.rows = table.rows.filter(Boolean).map(r => {
delete r[fieldName];
return r;
});
Expand Down
2 changes: 1 addition & 1 deletion src/app/tableThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export const loadTable = createAsyncThunk<
});
const data = await response.json();
if (data.status === 'success') {
const rows = data.rows;
const rows = (data.rows || []).filter(Boolean);
const names = rows.length > 0 ? Object.keys(rows[0]) : [];
const totalCount: number = data.total_row_count ?? rows.length;
originalRowCount = totalCount;
Expand Down
7 changes: 4 additions & 3 deletions src/components/ComponentType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,18 @@ export function createDictTable(
source: DataSourceConfig | undefined = undefined,
) : DictTable {

let names = Object.keys(rows[0])
const safeRows = rows.filter(Boolean);
let names = safeRows.length > 0 ? Object.keys(safeRows[0]) : [];

return {
id,
displayId: `${id}`,
names,
rows,
rows: safeRows,
metadata: names.reduce((acc, name) => ({
...acc,
[name]: {
type: inferTypeFromValueArray(rows.map(r => r[name])),
type: inferTypeFromValueArray(safeRows.map(r => r[name])),
semanticType: "",
levels: []
}
Expand Down
4 changes: 2 additions & 2 deletions src/views/DBTableManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ export const DataLoaderForm: React.FC<{
const previewTable: DictTable | null = useMemo(() => {
if (!selectedPreviewTable || !tableMetadata[selectedPreviewTable]) return null;
const metadata = tableMetadata[selectedPreviewTable];
const sampleRows = metadata.sample_rows || [];
const sampleRows = (metadata.sample_rows || []).filter(Boolean);
const columns = metadata.columns || [];
const names = columns.map((c: any) => c.name);
return {
Expand Down Expand Up @@ -644,7 +644,7 @@ export const DataLoaderForm: React.FC<{
}
}

const sampleRows = metadata.sample_rows || [];
const sampleRows = (metadata.sample_rows || []).filter(Boolean);
const columns = metadata.columns || [];
const tableObj: DictTable = {
id: tableName.split('.').pop() || tableName,
Expand Down
6 changes: 3 additions & 3 deletions src/views/DataView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ export const FreeDataViewFC: FC<FreeDataViewProps> = function DataView() {
let rowData = [];
if (targetTable) {
if (targetTable.virtual) {
rowData = targetTable.rows;
rowData = targetTable.rows.filter(Boolean);
} else {
rowData = targetTable.rows;
rowData = targetTable.rows.filter(Boolean);
rowData = rowData.map((r: any, i: number) => ({ ...r, "#rowId": i }));
}
}
Expand All @@ -66,7 +66,7 @@ export const FreeDataViewFC: FC<FreeDataViewProps> = function DataView() {
if (name === "#rowId") return { minWidth: 10, width: 40 }; // Default for row ID column

// Get all values for this column from sampled rows
const values = sampledRows.map(row => String(row[name] || ''));
const values = sampledRows.filter(Boolean).map(row => String(row[name] || ''));

// Estimate width based on content length (simple approach)
const avgLength = values.length > 0
Expand Down
2 changes: 1 addition & 1 deletion src/views/EncodingBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export const EncodingBox: FC<EncodingBoxProps> = function EncodingBox({ channel,

let stackOpt: any[] = [];

let domainItems = (field && activeTable) ? activeTable.rows.map(row => row[field!.name]) : [];
let domainItems = (field && activeTable) ? activeTable.rows.filter(Boolean).map(row => row[field!.name]) : [];
domainItems = [...new Set(domainItems)];

let autoSortEnabled = field && fieldMetadata?.type == Type.String && domainItems.length < 200;
Expand Down
1 change: 1 addition & 0 deletions src/views/ReactTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const CustomReactTable: React.FC<CustomReactTableProps> = ({
</TableHead>
<TableBody>
{rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.filter(Boolean)
.map((row, i) => {
return (
<TableRow hover tabIndex={-1} key={i} sx={{ background: i % 2 == 0 ? '#F0F0F0' : "none" }}>
Comment on lines 88 to 92
Expand Down
6 changes: 5 additions & 1 deletion src/views/RefreshDataDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ export const RefreshDataDialog: React.FC<RefreshDataDialogProps> = ({
return { valid: false, message: 'No data found in the uploaded content.' };
}

const newColumns = Object.keys(newRows[0]).sort();
const firstRow = newRows.find(Boolean);
if (!firstRow) {
return { valid: false, message: 'No valid data rows found in the uploaded content.' };
}
const newColumns = Object.keys(firstRow).sort();
const existingColumns = [...table.names].sort();

if (newColumns.length !== existingColumns.length) {
Expand Down
10 changes: 6 additions & 4 deletions src/views/SelectableDataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({

let theme = useTheme();

const [rowsToDisplay, setRowsToDisplay] = React.useState<any[]>(rows);
const [rowsToDisplay, setRowsToDisplay] = React.useState<any[]>((rows || []).filter(Boolean));

// Initialize as true to cover the initial mount delay
const [isLoading, setIsLoading] = React.useState<boolean>(true);
Expand All @@ -273,10 +273,11 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({
}, []);

React.useEffect(() => {
const safeRows = (rows || []).filter(Boolean);
if (orderBy && !isLoading) {
setRowsToDisplay(rows.slice().sort(getComparator(order, orderBy)));
setRowsToDisplay(safeRows.slice().sort(getComparator(order, orderBy)));
} else {
setRowsToDisplay(rows);
setRowsToDisplay(safeRows);
}
}, [rows, order, orderBy])

Expand Down Expand Up @@ -358,7 +359,7 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
setRowsToDisplay(data.rows);
setRowsToDisplay((data.rows || []).filter(Boolean));
}
// Set loading to false when done
setIsLoading(false);
Expand Down Expand Up @@ -451,6 +452,7 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({
)
}}
itemContent={(rowIndex, data) => {
if (!data) return null;
return (
<>
{columnDefs.map((column, colIndex) => {
Expand Down
19 changes: 11 additions & 8 deletions src/views/VisualizationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,12 @@ export let renderTableChart = (
return encoding.fieldID != undefined;
}).map(([channel, encoding]) => conceptShelfItems.find(f => f.id == encoding.fieldID) as FieldItem);

if (fields.length == 0) {
fields = conceptShelfItems.filter(f => Object.keys(extTable[0]).includes(f.name));
const safeExtTable = extTable.filter(Boolean);
if (fields.length == 0 && safeExtTable.length > 0) {
fields = conceptShelfItems.filter(f => Object.keys(safeExtTable[0]).includes(f.name));
}

let rows = extTable.map(row => Object.fromEntries(fields.filter(f => Object.keys(row).includes(f.name)).map(f => [f.name, row[f.name]])))
let rows = safeExtTable.map(row => Object.fromEntries(fields.filter(f => Object.keys(row).includes(f.name)).map(f => [f.name, row[f.name]])))

let colDefs = fields.map(field => {
let name = field.name;
Expand Down Expand Up @@ -193,15 +194,17 @@ export let checkChartAvailabilityOnPreparedData = (chart: Chart, conceptShelfIte
}
return undefined;
}).filter((f): f is string => f != undefined);
return visFieldsFinalNames.length > 0 && visTableRows.length > 0 && visFieldsFinalNames.every(name => Object.keys(visTableRows[0]).includes(name));
const firstRow = visTableRows.find(Boolean);
return visFieldsFinalNames.length > 0 && firstRow != null && visFieldsFinalNames.every(name => Object.keys(firstRow).includes(name));
}

export let checkChartAvailability = (chart: Chart, conceptShelfItems: FieldItem[], visTableRows: any[]) => {
let visFieldIds = Object.keys(chart.encodingMap)
.filter(key => chart.encodingMap[key as keyof EncodingMap].fieldID != undefined)
.map(key => chart.encodingMap[key as keyof EncodingMap].fieldID);
let visFields = conceptShelfItems.filter(f => visFieldIds.includes(f.id));
return visFields.length > 0 && visTableRows.length > 0 && visFields.every(f => Object.keys(visTableRows[0]).includes(f.name));
const firstRow = visTableRows.find(Boolean);
return visFields.length > 0 && firstRow != null && visFields.every(f => Object.keys(firstRow).includes(f.name));
}

export let SampleSizeEditor: FC<{
Expand Down Expand Up @@ -592,10 +595,10 @@ export const ChartEditorFC: FC<{}> = function ChartEditorFC({}) {

let createVisTableRowsLocal = (rows: any[]) => {
if (visFields.length == 0) {
return rows;
return rows.filter(Boolean);
}

let filteredRows = rows.map(row => Object.fromEntries(visFields.filter(f => table.names.includes(f.name)).map(f => [f.name, row[f.name]])));
let filteredRows = rows.filter(Boolean).map(row => Object.fromEntries(visFields.filter(f => table.names.includes(f.name)).map(f => [f.name, row[f.name]])));
let visTable = prepVisTable(filteredRows, conceptShelfItems, focusedChart.encodingMap);

if (visTable.length > serverConfig.MAX_DISPLAY_ROWS) {
Expand Down Expand Up @@ -664,7 +667,7 @@ export const ChartEditorFC: FC<{}> = function ChartEditorFC({}) {
if (currentRequestRef.current === requestId) {
const versionId = computeVersionId();
if (data.status == "success") {
setVisTableRows(data.rows);
setVisTableRows((data.rows || []).filter(Boolean));
setVisTableTotalRowCount(data.total_row_count);
setDataVersion(versionId);
// Cache for instant reuse on chart revisit
Expand Down
Loading