Skip to content

Commit

Permalink
feat: transport Exceptions from tasks over our websocket connection
Browse files Browse the repository at this point in the history
  • Loading branch information
neindochoh committed Oct 23, 2023
1 parent 9a648bd commit f114895
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 31 deletions.
2 changes: 2 additions & 0 deletions renumics/spotlight/backend/tasks/reduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ def compute_umap(
Prepare data from table and compute U-Map on them.
"""

raise Exception("Ooops")

try:
data, indices = align_data(data_store, column_names, indices)
except (ColumnNotExistsError, ColumnNotEmbeddable):
Expand Down
24 changes: 24 additions & 0 deletions renumics/spotlight/backend/websockets.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ class ReductionMessage(Message):
generation_id: int


class TaskErrorMessage(Message):
"""
Common message model for task errors
"""

widget_id: str
uid: str
error: str
title: str
detail: Optional[str] = None
data: None = None


class ReductionRequestData(BaseModel):
"""
Base data reduction request payload.
Expand Down Expand Up @@ -155,6 +168,7 @@ async def handle_message(request: Message, connection: "WebsocketConnection") ->
New message types should be registered by decorating with `@handle_message.register`.
"""

# TODO: add generic error handler
raise NotImplementedError


Expand Down Expand Up @@ -326,6 +340,16 @@ async def _(request: UMapRequest, connection: "WebsocketConnection") -> None:
)
except TaskCancelled:
...
except Exception as e:
msg = TaskErrorMessage(
type="tasks.error",
widget_id=request.widget_id,
uid=request.uid,
error=type(e).__name__,
title=type(e).__name__,
detail=type(e).__doc__,
)
await connection.send_async(msg)
else:
response = ReductionResponse(
type="umap_result",
Expand Down
12 changes: 11 additions & 1 deletion src/services/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useDataset } from '../stores/dataset';
import { useLayout } from '../stores/layout';
import { IndexArray } from '../types';

Check warning on line 3 in src/services/data.ts

View workflow job for this annotation

GitHub Actions / 🔍 Check

'/home/runner/work/spotlight/spotlight/src/types/index.ts' imported multiple times

Check warning on line 3 in src/services/data.ts

View workflow job for this annotation

GitHub Actions / 🔍 Check

'/home/runner/work/spotlight/spotlight/src/types/index.ts' imported multiple times
import { v4 as uuidv4 } from 'uuid';
import { Problem } from '../types';

Check warning on line 5 in src/services/data.ts

View workflow job for this annotation

GitHub Actions / 🔍 Check

'/home/runner/work/spotlight/spotlight/src/types/index.ts' imported multiple times

Check warning on line 5 in src/services/data.ts

View workflow job for this annotation

GitHub Actions / 🔍 Check

'/home/runner/work/spotlight/spotlight/src/types/index.ts' imported multiple times

export const umapMetricNames = [
'euclidean',
Expand Down Expand Up @@ -110,9 +111,18 @@ export class DataService {
): Promise<ReductionResult> {
const messageId = uuidv4();

const promise = new Promise<ReductionResult>((resolve) => {
const promise = new Promise<ReductionResult>((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.dispatchTable.set(messageId, (message: any) => {
if (message.type === 'tasks.error') {
const error: Problem = {
type: message.error,
title: message.title,
detail: message.detail,
};
reject(error);
return;
}
const result = {
points: message.data.points,
indices: message.data.indices,
Expand Down
84 changes: 54 additions & 30 deletions src/widgets/SimilarityMap/SimilarityMap.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SimilaritiesIcon from '../../icons/Bubbles';
import WarningIcon from '../../icons/Warning';

Check warning on line 2 in src/widgets/SimilarityMap/SimilarityMap.tsx

View workflow job for this annotation

GitHub Actions / 🔍 Check

'WarningIcon' is defined but never used

Check warning on line 2 in src/widgets/SimilarityMap/SimilarityMap.tsx

View workflow job for this annotation

GitHub Actions / 🔍 Check

'WarningIcon' is defined but never used
import LoadingIndicator from '../../components/LoadingIndicator';
import Plot, {
MergeStrategy,
Expand All @@ -23,6 +24,7 @@ import {
IndexArray,
isNumberColumn,
NumberColumn,
Problem,
TableData,
} from '../../types';
import { createSizeTransferFunction } from '../../dataformat';
Expand Down Expand Up @@ -220,8 +222,8 @@ const SimilarityMap: Widget = () => {
const placeByColumns = useDataset(placeByColumnsSelector, shallow);

const [positions, setPositions] = useState<[number, number][]>([]);

const [visibleIndices, setVisibleIndices] = useState<IndexArray>([]);
const [problem, setProblem] = useState<Problem | null>(null);

const selected = useMemo(() => {
const selected: boolean[] = [];
Expand Down Expand Up @@ -275,13 +277,14 @@ const SimilarityMap: Widget = () => {
const widgetId = useMemo(() => uuidv4(), []);

useEffect(() => {
setVisibleIndices([]);
setPositions([]);
setProblem(null);

if (!indices.length || !placeByColumnKeys.length) {
setVisibleIndices([]);
setPositions([]);
setIsComputing(false);
return;
}

setIsComputing(true);

const reductionPromise =
Expand All @@ -302,12 +305,18 @@ const SimilarityMap: Widget = () => {
);

let cancelled = false;
reductionPromise.then(({ points, indices }) => {
if (cancelled) return;
setVisibleIndices(indices);
setPositions(points);
setIsComputing(false);
});
reductionPromise
.then(({ points, indices }) => {
if (cancelled) return;
setVisibleIndices(indices);
setPositions(points);
setIsComputing(false);
})
.catch((problem: Problem) => {
if (cancelled) return;
setProblem(problem);
setIsComputing(false);
});

return () => {
cancelled = true;
Expand Down Expand Up @@ -443,23 +452,35 @@ const SimilarityMap: Widget = () => {
const areColumnsSelected = !!placeByColumnKeys.length;
const hasVisibleRows = !!visibleIndices.length;

return (
<MapContainer data-test-tag="similaritymap">
{isComputing && <LoadingIndicator />}
{!areColumnsSelected && !isComputing && (
<Info>
<span>
Select columns and show at least two rows to display similarity
map
</span>
</Info>
)}
{areColumnsSelected && !hasVisibleRows && !isComputing && (
<Info>
<span>Not enough rows</span>
</Info>
)}
{areColumnsSelected && hasVisibleRows && !isComputing && (
let content: JSX.Element;
if (problem) {
content = (
<div tw="w-full h-full flex flex-col items-stretch justify-end">
<div tw="bg-red-500 text-black/80 text-sm font-bold p-0.5">
{problem.title}
</div>
<div tw="bg-red-500 text-black/80 text-xs p-0.5">{problem.detail}</div>
</div>
);
} else if (isComputing) {
content = <LoadingIndicator />;
} else if (!areColumnsSelected) {
content = (
<Info>
<span>
Select columns and show at least two rows to display similarity map
</span>
</Info>
);
} else if (!hasVisibleRows) {
content = (
<Info>
<span>Not enough rows</span>
</Info>
);
} else {
content = (
<>
<PlotContainer>
<Plot
points={positions}
Expand All @@ -485,14 +506,17 @@ const SimilarityMap: Widget = () => {
/>
)}
</PlotContainer>
)}

{!isComputing && (
<div tw="absolute bottom-0 w-full text-xs text-right text-gray-700 p-0.5 pointer-events-none">
{visibleIndices.length} of {indices.length} rows
</div>
)}
</>
);
}

return (
<MapContainer data-test-tag="similaritymap">
{content}
<MenuBar
colorBy={colorByKey}
sizeBy={sizeByKey}
Expand Down

0 comments on commit f114895

Please sign in to comment.