Add model evaluation run to store and view (#3385)

This commit is contained in:
Charis Kyriakou
2024-02-22 16:28:32 +00:00
committed by GitHub
parent 07d9c4efc4
commit 948c1e2eb7
9 changed files with 188 additions and 16 deletions

View File

@@ -25,6 +25,7 @@ import type {
UrlValueResolvable,
} from "./raw-result-types";
import type { AccessPathSuggestionOptions } from "../model-editor/suggestions";
import type { ModelEvaluationRunState } from "../model-editor/shared/model-evaluation-run-state";
/**
* This module contains types and code that are shared between
@@ -638,6 +639,11 @@ interface SetAccessPathSuggestionsMessage {
accessPathSuggestions: AccessPathSuggestionOptions;
}
interface SetModelEvaluationRunMessage {
t: "setModelEvaluationRun";
run: ModelEvaluationRunState | undefined;
}
export type ToModelEditorMessage =
| SetExtensionPackStateMessage
| SetMethodsMessage
@@ -646,7 +652,8 @@ export type ToModelEditorMessage =
| SetInProgressMethodsMessage
| SetProcessedByAutoModelMethodsMessage
| RevealMethodMessage
| SetAccessPathSuggestionsMessage;
| SetAccessPathSuggestionsMessage
| SetModelEvaluationRunMessage;
export type FromModelEditorMessage =
| CommonFromViewMessages

View File

@@ -57,12 +57,15 @@ import { LSPErrorCodes } from "vscode-languageclient";
import type { AccessPathSuggestionOptions } from "./suggestions";
import { runSuggestionsQuery } from "./suggestion-queries";
import { parseAccessPathSuggestionRowsToOptions } from "./suggestions-bqrs";
import { ModelEvaluator } from "./model-evaluator";
import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state";
export class ModelEditorView extends AbstractWebview<
ToModelEditorMessage,
FromModelEditorMessage
> {
private readonly autoModeler: AutoModeler;
private readonly modelEvaluator: ModelEvaluator;
private readonly languageDefinition: ModelsAsDataLanguage;
public constructor(
@@ -101,6 +104,14 @@ export class ModelEditorView extends AbstractWebview<
},
);
this.languageDefinition = getModelsAsDataLanguage(language);
this.modelEvaluator = new ModelEvaluator(
modelingStore,
modelingEvents,
databaseItem,
this.updateModelEvaluationRun.bind(this),
);
this.push(this.modelEvaluator);
}
public async openView() {
@@ -338,10 +349,10 @@ export class ModelEditorView extends AbstractWebview<
break;
}
case "startModelEvaluation":
this.startModelEvaluation();
await this.modelEvaluator.startEvaluation();
break;
case "stopModelEvaluation":
this.stopModelEvaluation();
await this.modelEvaluator.stopEvaluation();
break;
case "telemetry":
telemetryListener?.sendUIInteraction(msg.action);
@@ -920,11 +931,10 @@ export class ModelEditorView extends AbstractWebview<
this.modelingStore.addModifiedMethod(this.databaseItem, signature);
}
private startModelEvaluation() {
// Do nothing for now. This will be fleshed out in the near future.
}
private stopModelEvaluation() {
// Do nothing for now. This will be fleshed out in the near future.
private async updateModelEvaluationRun(run: ModelEvaluationRunState) {
await this.postMessage({
t: "setModelEvaluationRun",
run,
});
}
}

View File

@@ -0,0 +1,4 @@
export interface ModelEvaluationRun {
isPreparing: boolean;
variantAnalysisId: number | undefined;
}

View File

@@ -0,0 +1,73 @@
import type { ModelingStore } from "./modeling-store";
import type { ModelingEvents } from "./modeling-events";
import type { DatabaseItem } from "../databases/local-databases";
import type { ModelEvaluationRun } from "./model-evaluation-run";
import { DisposableObject } from "../common/disposable-object";
import { sleep } from "../common/time";
import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state";
export class ModelEvaluator extends DisposableObject {
public constructor(
private readonly modelingStore: ModelingStore,
private readonly modelingEvents: ModelingEvents,
private readonly dbItem: DatabaseItem,
private readonly updateView: (
run: ModelEvaluationRunState,
) => Promise<void>,
) {
super();
this.registerToModelingEvents();
}
public async startEvaluation() {
// Update store with evaluation run status
const evaluationRun: ModelEvaluationRun = {
isPreparing: true,
variantAnalysisId: undefined,
};
this.modelingStore.updateModelEvaluationRun(this.dbItem, evaluationRun);
// For now, just wait 5 seconds and then update the store.
// In the future, this will be replaced with the actual evaluation process.
void sleep(5000).then(() => {
const completedEvaluationRun: ModelEvaluationRun = {
isPreparing: false,
variantAnalysisId: undefined,
};
this.modelingStore.updateModelEvaluationRun(
this.dbItem,
completedEvaluationRun,
);
});
}
public async stopEvaluation() {
// For now just update the store.
// This will be fleshed out in the near future.
const evaluationRun: ModelEvaluationRun = {
isPreparing: false,
variantAnalysisId: undefined,
};
this.modelingStore.updateModelEvaluationRun(this.dbItem, evaluationRun);
}
private registerToModelingEvents() {
this.push(
this.modelingEvents.onModelEvaluationRunChanged(async (event) => {
if (
event.evaluationRun &&
event.dbUri === this.dbItem.databaseUri.toString()
) {
const run: ModelEvaluationRunState = {
isPreparing: event.evaluationRun.isPreparing,
// TODO: Get variant analysis from id.
variantAnalysis: undefined,
};
await this.updateView(run);
}
}),
);
}
}

View File

@@ -3,6 +3,7 @@ import { DisposableObject } from "../common/disposable-object";
import type { AppEvent, AppEventEmitter } from "../common/events";
import type { DatabaseItem } from "../databases/local-databases";
import type { Method, Usage } from "./method";
import type { ModelEvaluationRun } from "./model-evaluation-run";
import type { ModeledMethod } from "./modeled-method";
import type { Mode } from "./shared/mode";
@@ -55,6 +56,11 @@ interface ProcessedByAutoModelMethodsChangedEvent {
readonly methods: ReadonlySet<string>;
}
interface ModelEvaluationRunChangedEvent {
readonly dbUri: string;
readonly evaluationRun: ModelEvaluationRun | undefined;
}
interface RevealInModelEditorEvent {
dbUri: string;
method: Method;
@@ -76,6 +82,7 @@ export class ModelingEvents extends DisposableObject {
public readonly onSelectedMethodChanged: AppEvent<SelectedMethodChangedEvent>;
public readonly onInProgressMethodsChanged: AppEvent<InProgressMethodsChangedEvent>;
public readonly onProcessedByAutoModelMethodsChanged: AppEvent<ProcessedByAutoModelMethodsChangedEvent>;
public readonly onModelEvaluationRunChanged: AppEvent<ModelEvaluationRunChangedEvent>;
public readonly onRevealInModelEditor: AppEvent<RevealInModelEditorEvent>;
public readonly onFocusModelEditor: AppEvent<FocusModelEditorEvent>;
@@ -90,6 +97,7 @@ export class ModelingEvents extends DisposableObject {
private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter<SelectedMethodChangedEvent>;
private readonly onInProgressMethodsChangedEventEmitter: AppEventEmitter<InProgressMethodsChangedEvent>;
private readonly onProcessedByAutoModelMethodsChangedEventEmitter: AppEventEmitter<ProcessedByAutoModelMethodsChangedEvent>;
private readonly onModelEvaluationRunChangedEventEmitter: AppEventEmitter<ModelEvaluationRunChangedEvent>;
private readonly onRevealInModelEditorEventEmitter: AppEventEmitter<RevealInModelEditorEvent>;
private readonly onFocusModelEditorEventEmitter: AppEventEmitter<FocusModelEditorEvent>;
@@ -155,6 +163,12 @@ export class ModelingEvents extends DisposableObject {
this.onProcessedByAutoModelMethodsChanged =
this.onProcessedByAutoModelMethodsChangedEventEmitter.event;
this.onModelEvaluationRunChangedEventEmitter = this.push(
app.createEventEmitter<ModelEvaluationRunChangedEvent>(),
);
this.onModelEvaluationRunChanged =
this.onModelEvaluationRunChangedEventEmitter.event;
this.onRevealInModelEditorEventEmitter = this.push(
app.createEventEmitter<RevealInModelEditorEvent>(),
);
@@ -273,6 +287,16 @@ export class ModelingEvents extends DisposableObject {
});
}
public fireModelEvaluationRunChangedEvent(
dbUri: string,
evaluationRun: ModelEvaluationRun | undefined,
) {
this.onModelEvaluationRunChangedEventEmitter.fire({
dbUri,
evaluationRun,
});
}
public fireRevealInModelEditorEvent(dbUri: string, method: Method) {
this.onRevealInModelEditorEventEmitter.fire({
dbUri,

View File

@@ -1,6 +1,7 @@
import { DisposableObject } from "../common/disposable-object";
import type { DatabaseItem } from "../databases/local-databases";
import type { Method, Usage } from "./method";
import type { ModelEvaluationRun } from "./model-evaluation-run";
import type { ModeledMethod } from "./modeled-method";
import type { ModelingEvents } from "./modeling-events";
import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "./shared/hide-modeled-methods";
@@ -17,6 +18,7 @@ interface InternalDbModelingState {
processedByAutoModelMethods: Set<string>;
selectedMethod: Method | undefined;
selectedUsage: Usage | undefined;
modelEvaluationRun: ModelEvaluationRun | undefined;
}
interface DbModelingState {
@@ -30,6 +32,7 @@ interface DbModelingState {
readonly processedByAutoModelMethods: ReadonlySet<string>;
readonly selectedMethod: Method | undefined;
readonly selectedUsage: Usage | undefined;
readonly modelEvaluationRun: ModelEvaluationRun | undefined;
}
interface SelectedMethodDetails {
@@ -66,6 +69,7 @@ export class ModelingStore extends DisposableObject {
selectedMethod: undefined,
selectedUsage: undefined,
inProgressMethods: new Set(),
modelEvaluationRun: undefined,
});
this.modelingEvents.fireDbOpenedEvent(databaseItem);
@@ -372,6 +376,15 @@ export class ModelingStore extends DisposableObject {
});
}
public updateModelEvaluationRun(
dbItem: DatabaseItem,
evaluationRun: ModelEvaluationRun,
) {
this.changeModelEvaluationRun(dbItem, (state) => {
state.modelEvaluationRun = evaluationRun;
});
}
public getSelectedMethodDetails(): SelectedMethodDetails | undefined {
const dbState = this.getInternalStateForActiveDb();
if (!dbState) {
@@ -465,4 +478,18 @@ export class ModelingStore extends DisposableObject {
state.processedByAutoModelMethods,
);
}
private changeModelEvaluationRun(
dbItem: DatabaseItem,
updateState: (state: InternalDbModelingState) => void,
) {
const state = this.getState(dbItem);
updateState(state);
this.modelingEvents.fireModelEvaluationRunChangedEvent(
dbItem.databaseUri.toString(),
state.modelEvaluationRun,
);
}
}

View File

@@ -0,0 +1,19 @@
import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis";
import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis";
export interface ModelEvaluationRunState {
isPreparing: boolean;
variantAnalysis: VariantAnalysis | undefined;
}
export function modelEvaluationRunIsRunning(
run: ModelEvaluationRunState,
): boolean {
return (
run.isPreparing ||
!!(
run.variantAnalysis &&
run.variantAnalysis.status === VariantAnalysisStatus.InProgress
)
);
}

View File

@@ -20,6 +20,8 @@ import { Mode } from "../../model-editor/shared/mode";
import { getLanguageDisplayName } from "../../common/query-language";
import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "../../model-editor/shared/hide-modeled-methods";
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
import { modelEvaluationRunIsRunning } from "../../model-editor/shared/model-evaluation-run-state";
const LoadingContainer = styled.div`
text-align: center;
@@ -87,20 +89,20 @@ const ModelEvaluation = ({
modifiedSignatures,
onStartEvaluation,
onStopEvaluation,
evaluationInProgress,
evaluationRun,
}: {
viewState: ModelEditorViewState;
modeledMethods: Record<string, ModeledMethod[]>;
modifiedSignatures: Set<string>;
onStartEvaluation: () => void;
onStopEvaluation: () => void;
evaluationInProgress: boolean;
evaluationRun: ModelEvaluationRunState | undefined;
}) => {
if (!viewState.showEvaluationUi) {
return null;
}
if (!evaluationInProgress) {
if (!evaluationRun || !modelEvaluationRunIsRunning(evaluationRun)) {
const customModelsExist = Object.values(modeledMethods).some(
(methods) => methods.filter((m) => m.type !== "none").length > 0,
);
@@ -166,7 +168,9 @@ export function ModelEditor({
string | null
>(null);
const [evaluationInProgress, setEvaluationInProgress] = useState(false);
const [evaluationRun, setEvaluationRun] = useState<
ModelEvaluationRunState | undefined
>(undefined);
useEffect(() => {
vscode.postMessage({
@@ -214,6 +218,9 @@ export function ModelEditor({
case "setAccessPathSuggestions":
setAccessPathSuggestions(msg.accessPathSuggestions);
break;
case "setModelEvaluationRun":
setEvaluationRun(msg.run);
break;
default:
assertNever(msg);
}
@@ -309,14 +316,12 @@ export function ModelEditor({
);
const onStartEvaluation = useCallback(() => {
setEvaluationInProgress(true);
vscode.postMessage({
t: "startModelEvaluation",
});
}, []);
const onStopEvaluation = useCallback(() => {
setEvaluationInProgress(false);
vscode.postMessage({
t: "stopModelEvaluation",
});
@@ -447,7 +452,7 @@ export function ModelEditor({
modifiedSignatures={modifiedSignatures}
onStartEvaluation={onStartEvaluation}
onStopEvaluation={onStopEvaluation}
evaluationInProgress={evaluationInProgress}
evaluationRun={evaluationRun}
/>
</ButtonsContainer>
</HeaderRow>

View File

@@ -13,6 +13,7 @@ export function createMockModelingEvents({
onProcessedByAutoModelMethodsChanged = jest.fn(),
onRevealInModelEditor = jest.fn(),
onFocusModelEditor = jest.fn(),
onModelEvaluationRunChanged = jest.fn(),
}: {
onActiveDbChanged?: ModelingEvents["onActiveDbChanged"];
onDbClosed?: ModelingEvents["onDbClosed"];
@@ -25,6 +26,7 @@ export function createMockModelingEvents({
onProcessedByAutoModelMethodsChanged?: ModelingEvents["onProcessedByAutoModelMethodsChanged"];
onRevealInModelEditor?: ModelingEvents["onRevealInModelEditor"];
onFocusModelEditor?: ModelingEvents["onFocusModelEditor"];
onModelEvaluationRunChanged?: ModelingEvents["onModelEvaluationRunChanged"];
} = {}): ModelingEvents {
return mockedObject<ModelingEvents>({
onActiveDbChanged,
@@ -38,5 +40,6 @@ export function createMockModelingEvents({
onProcessedByAutoModelMethodsChanged,
onRevealInModelEditor,
onFocusModelEditor,
onModelEvaluationRunChanged,
});
}