Skip to content

Commit

Permalink
Add mutation region detail coordinate information and filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
jonlamb-gh committed Jan 23, 2024
1 parent 017d5c3 commit 1164142
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 26 deletions.
2 changes: 1 addition & 1 deletion vscode/generated/src/modality-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ export interface components {
percentage_specs_passing: number;
};
EventCoordinate: {
opaque_event_id?: (number)[];
id?: (number)[];
timeline_id?: components["schemas"]["TimelineId"];
};
EventSummary: {
Expand Down
28 changes: 28 additions & 0 deletions vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@
"command": "auxon.mutations.clearMutation",
"when": "view == auxon.mutations && viewItem == mutation",
"group": "inline"
},
{
"command": "auxon.mutations.viewLogFromMutation",
"when": "view == auxon.mutations && viewItem == mutationCoordinate",
"group": "inline"
}
],
"view/title": [
Expand Down Expand Up @@ -472,6 +477,16 @@
"command": "auxon.mutations.disableMutationFiltering",
"when": "view == auxon.mutations && auxon.mutations.filterBy == 'MUTATOR_ID'",
"group": "7_modification@3"
},
{
"command": "auxon.mutations.showCleared",
"when": "view == auxon.mutations && auxon.mutations.cleared == 'HIDE'",
"group": "7_modification@1"
},
{
"command": "auxon.mutations.hideCleared",
"when": "view == auxon.mutations && auxon.mutations.cleared == 'SHOW'",
"group": "7_modification@1"
}
]
},
Expand Down Expand Up @@ -786,10 +801,23 @@
"command": "auxon.mutations.disableMutationFiltering",
"title": "✓ Filter By Selected Mutator"
},
{
"command": "auxon.mutations.showCleared",
"title": "Show Cleared Mutations"
},
{
"command": "auxon.mutations.hideCleared",
"title": "✓ Show Cleared Mutations"
},
{
"command": "auxon.mutations.clearMutation",
"title": "Clear Mutation",
"icon": "$(notebook-state-error)"
},
{
"command": "auxon.mutations.viewLogFromMutation",
"title": "View Log from this Coordinate",
"icon": "$(open-preview)"
}
],
"configuration": {
Expand Down
2 changes: 1 addition & 1 deletion vscode/src/modality-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -2283,7 +2283,7 @@
"EventCoordinate": {
"type": "object",
"properties": {
"opaque_event_id": {
"id": {
"type": "array",
"items": {
"type": "integer",
Expand Down
19 changes: 9 additions & 10 deletions vscode/src/modalityApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type CaseCoverage = gen.components["schemas"]["CaseCoverage"];
export type CoverageAggregates = gen.components["schemas"]["CoverageAggregates"];
export type LogicalTime = gen.components["schemas"]["LogicalTime"];
export type Nanoseconds = gen.components["schemas"]["Nanoseconds"];
export type EventCoordinate = gen.components["schemas"]["EventCoordinate"];
export type SegmentationRuleName = gen.components["schemas"]["SegmentationRuleName"];
export type SpecContent = gen.components["schemas"]["SpecContent"];
export type SpecStructure = gen.components["schemas"]["SpecStructure"];
Expand All @@ -50,6 +51,7 @@ export type MutatorState = gen.components["schemas"]["MutatorState"];
export type Mutator = gen.components["schemas"]["Mutator"];
export type MutationId = gen.components["schemas"]["MutatorId"];
export type Mutation = gen.components["schemas"]["Mutation"];
export type MutationRegionDetails = gen.components["schemas"]["MutationRegionDetails"];

type InternalClient = ReturnType<typeof createClient<gen.paths>>;

Expand Down Expand Up @@ -318,16 +320,13 @@ export class SegmentClient {
if (typeof mutatorId !== "undefined") {
q.push(["mutator_id", mutatorId]);
}
const res = await this.client.get(
"/v2/mutations/{workspace_version_id}/segments/{rule_name}/{segment_name}",
{
params: {
path: this.segmentId,
// @ts-ignore
query: q,
},
}
);
const res = await this.client.get("/v2/mutations/{workspace_version_id}/segments/{rule_name}/{segment_name}", {
params: {
path: this.segmentId,
// @ts-ignore
query: q,
},
});
return unwrapData(res);
}
}
Expand Down
155 changes: 141 additions & 14 deletions vscode/src/mutations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as vscode from "vscode";
import * as api from "./modalityApi";
import * as modalityLog from "./modalityLog";

class MutationsTreeMemento {
constructor(private readonly memento: vscode.Memento) {}
Expand All @@ -19,6 +20,14 @@ class MutationsTreeMemento {
async setFilterBySelectedMutator(val: boolean): Promise<void> {
return this.memento.update("mutationsTree_filterBySelectedMutator", val);
}

getShowClearedMutations(): boolean {
return this.memento.get("mutationsTree_showClearedMutations", false);
}

async setShowClearedMutations(val: boolean): Promise<void> {
return this.memento.update("mutationsTree_showClearedMutations", val);
}
}

export class MutationsTreeDataProvider implements vscode.TreeDataProvider<MutationsTreeItemData> {
Expand Down Expand Up @@ -63,9 +72,14 @@ export class MutationsTreeDataProvider implements vscode.TreeDataProvider<Mutati
vscode.commands.registerCommand("auxon.mutations.filterBySelectedMutator", () => {
this.filterBySelectedMutator();
}),
vscode.commands.registerCommand("auxon.mutations.showCleared", () => this.showClearedMutations(true)),
vscode.commands.registerCommand("auxon.mutations.hideCleared", () => this.showClearedMutations(false)),
vscode.commands.registerCommand("auxon.mutations.clearMutation", (itemData) => {
this.clearMutation(itemData);
})
}),
vscode.commands.registerCommand("auxon.mutations.viewLogFromMutation", (itemData) =>
this.viewLogFromMutation(itemData)
)
);

this.refresh();
Expand All @@ -82,6 +96,11 @@ export class MutationsTreeDataProvider implements vscode.TreeDataProvider<Mutati
"auxon.mutations.filterBy",
this.workspaceState.getFilterBySelectedMutator() ? "MUTATOR_ID" : "NONE"
);
vscode.commands.executeCommand(
"setContext",
"auxon.mutations.cleared",
this.workspaceState.getShowClearedMutations() ? "SHOW" : "HIDE"
);

this._onDidChangeTreeData.fire(undefined);
}
Expand All @@ -102,23 +121,29 @@ export class MutationsTreeDataProvider implements vscode.TreeDataProvider<Mutati
let mutations = [];

if (this.workspaceState.getFilterBySelectedMutator()) {
mutations = await this.apiClient.segment(this.activeSegmentId).mutations(this.selectedMutatorId)
mutations = await this.apiClient.segment(this.activeSegmentId).mutations(this.selectedMutatorId);
} else {
mutations = await this.apiClient.segment(this.activeSegmentId).mutations()
mutations = await this.apiClient.segment(this.activeSegmentId).mutations();
}

mutations = mutations.map((m) => new Mutation(m));
if (!this.workspaceState.getShowClearedMutations()) {
mutations = mutations.filter((m) => !m.wasCleared());
}

this.data = [];
if (this.workspaceState.getGroupByMutatorName()) {
const root = new MutationsGroupByNameTreeItemData("", []);
for (const m of mutations) {
root.insertNode(new Mutation(m));
root.insertNode(m);
}
root.updateDescriptions();
root.sortMutationsByCreatedAt();
const { compare } = Intl.Collator("en-US");
this.data = root.children().sort((a, b) => compare(a.name, b.name));
this.data = await root.children(this.apiClient);
this.data.sort((a, b) => compare(a.name, b.name));
} else {
this.data = mutations.map((m) => new NamedMutationTreeItemData(new Mutation(m)));
this.data = mutations.map((m) => new NamedMutationTreeItemData(m));
this.data.sort((a, b) => {
if (!(a instanceof NamedMutationTreeItemData) || !(b instanceof NamedMutationTreeItemData)) {
throw new Error("Internal error: mutations tree node not of expected type");
Expand All @@ -129,7 +154,7 @@ export class MutationsTreeDataProvider implements vscode.TreeDataProvider<Mutati
}
return this.data;
} else {
return element.children();
return await element.children(this.apiClient);
}
}

Expand Down Expand Up @@ -208,12 +233,37 @@ export class MutationsTreeDataProvider implements vscode.TreeDataProvider<Mutati
this.refresh();
}

showClearedMutations(show: boolean) {
this.workspaceState.setShowClearedMutations(show);
this.refresh();
}

clearMutation(item: MutationsTreeItemData) {
if (!(item instanceof NamedMutationTreeItemData)) {
throw new Error("Internal error: mutations tree node not of expected type");
}
vscode.commands.executeCommand("auxon.deviant.clearMutation", { mutationId: item.mutationId });
}

viewLogFromMutation(item: MutationsTreeItemData) {
if (item instanceof MutationCoordinateTreeItemData) {
// Encode the opaque_event_id as a string for the log command
let eventIdStr = "";
const opaque_event_id = item.coordinate.id;
for (const octet of opaque_event_id) {
if (octet != 0) {
eventIdStr += octet.toString(16).padStart(2, "0");
}
}
const literalTimelineId = "%" + item.coordinate.timeline_id.replace(/-/g, "");
vscode.commands.executeCommand(
"auxon.modality.log",
new modalityLog.ModalityLogCommandArgs({
from: `${literalTimelineId}:${eventIdStr}`,
})
);
}
}
}

// This is the base of all the tree item data classes
Expand Down Expand Up @@ -258,7 +308,7 @@ abstract class MutationsTreeItemData {
return false;
}

children(): MutationsTreeItemData[] {
async children(_apiClient: api.Client): Promise<MutationsTreeItemData[]> {
return [];
}
}
Expand All @@ -276,7 +326,7 @@ export class MutationsGroupByNameTreeItemData extends MutationsTreeItemData {
return true;
}

override children(): MutationsTreeItemData[] {
override async children(_apiClient: api.Client): Promise<MutationsTreeItemData[]> {
return this.childItems;
}

Expand Down Expand Up @@ -348,12 +398,46 @@ export class NamedMutationTreeItemData extends MutationsTreeItemData {
return true;
}

override children(): MutationsTreeItemData[] {
override async children(apiClient: api.Client): Promise<MutationsTreeItemData[]> {
const children = [];
children.push(new MutationDetailLeafTreeItemData(`Created At: ${this.mutation.createdAt}`));
if (this.mutation.experimentName) {
children.push(new MutationDetailLeafTreeItemData(`Experiment: ${this.mutation.experimentName}`));
}

if (this.mutation.regionDetailsSummary && this.mutation.regionDetailsSummary.command_communicated_and_success) {
const coord = this.mutation.regionDetailsSummary.command_communicated_and_success[0];
const maybeSuccess = this.mutation.regionDetailsSummary.command_communicated_and_success[1];
const timeline = await apiClient.timeline(coord.timeline_id).get();
let timelineStr = `${coord.timeline_id}`;
if (Object.prototype.hasOwnProperty.call(timeline.attributes, "timeline.name")) {
timelineStr = timeline.attributes["timeline.name"] as string;
}
children.push(
new MutationCoordinateTreeItemData(`Communicated Timeline: ${timelineStr}`, coord, maybeSuccess)
);
}
if (this.mutation.regionDetailsSummary && this.mutation.regionDetailsSummary.inject_attempted_and_success) {
const coord = this.mutation.regionDetailsSummary.inject_attempted_and_success[0];
const maybeSuccess = this.mutation.regionDetailsSummary.inject_attempted_and_success[1];
const timeline = await apiClient.timeline(coord.timeline_id).get();
let timelineStr = `${coord.timeline_id}`;
if (Object.prototype.hasOwnProperty.call(timeline.attributes, "timeline.name")) {
timelineStr = timeline.attributes["timeline.name"] as string;
}
children.push(new MutationCoordinateTreeItemData(`Injected Timeline: ${timelineStr}`, coord, maybeSuccess));
}
if (this.mutation.regionDetailsSummary && this.mutation.regionDetailsSummary.clear_communicated_and_success) {
const coord = this.mutation.regionDetailsSummary.clear_communicated_and_success[0];
const maybeSuccess = this.mutation.regionDetailsSummary.clear_communicated_and_success[1];
const timeline = await apiClient.timeline(coord.timeline_id).get();
let timelineStr = `${coord.timeline_id}`;
if (Object.prototype.hasOwnProperty.call(timeline.attributes, "timeline.name")) {
timelineStr = timeline.attributes["timeline.name"] as string;
}
children.push(new MutationCoordinateTreeItemData(`Cleared Timeline: ${timelineStr}`, coord, maybeSuccess));
}

if (this.mutation.params.size != 0) {
children.push(new MutationParametersTreeItemData(this.mutation));
}
Expand All @@ -365,13 +449,14 @@ export class MutationParametersTreeItemData extends MutationsTreeItemData {
contextValue = "mutationParameters";
constructor(public mutation: Mutation) {
super("Parameters");
super.iconPath = new vscode.ThemeIcon("output");
}

override canHaveChildren(): boolean {
return true;
}

override children(): MutationsTreeItemData[] {
override async children(_apiClient: api.Client): Promise<MutationsTreeItemData[]> {
const children = [];
for (const [paramName, paramValue] of this.mutation.params) {
children.push(new MutationDetailLeafTreeItemData(`${paramName}: ${paramValue}`));
Expand All @@ -380,16 +465,45 @@ export class MutationParametersTreeItemData extends MutationsTreeItemData {
}
}

export class MutationCoordinateTreeItemData extends MutationsTreeItemData {
contextValue = "mutationCoordinate";
constructor(public name: string, public coordinate: api.EventCoordinate, public maybeSuccess?: boolean) {
super(name);
this.iconPath = new vscode.ThemeIcon("git-commit");
}

override canHaveChildren(): boolean {
return true;
}

override async children(_apiClient: api.Client): Promise<MutationsTreeItemData[]> {
const children = [];

let msg = "NA";
let icon = new vscode.ThemeIcon("question", new vscode.ThemeColor("testing.iconQueued"));
if (this.maybeSuccess === true) {
msg = "Yes";
icon = new vscode.ThemeIcon("verified-filled", new vscode.ThemeColor("testing.iconPassed"));
} else if (this.maybeSuccess === false) {
msg = "No";
icon = new vscode.ThemeIcon("testing-failed-icon", new vscode.ThemeColor("testing.iconFailed"));
}

const item = new MutationDetailLeafTreeItemData(`Was Successful: ${msg}`);
item.iconPath = icon;
children.push(item);

return children;
}
}

export class MutationDetailLeafTreeItemData extends MutationsTreeItemData {
contextValue = "mutationDetail";
constructor(public name: string) {
super(name);
}
}

// TODO
// - add created-at datetime, sort the list
// - add checklist coordinate fields
class Mutation {
mutatorId: api.MutatorId;
mutatorName = "<unnamed>";
Expand All @@ -398,6 +512,9 @@ class Mutation {
createdAt: Date;
experimentName?: string = undefined;
params: Map<string, api.AttrVal>;
// N.B. currently just surfacing the overall summary, could consider
// the per-region summary if we expand beyond single segments
regionDetailsSummary?: api.MutationRegionDetails;

constructor(private mutation: api.Mutation) {
this.mutatorId = mutation.mutator_id;
Expand All @@ -417,5 +534,15 @@ class Mutation {
for (const [paramName, paramValue] of Object.entries(mutation.params)) {
this.params.set(paramName, paramValue);
}
if (mutation.region_details_summary) {
this.regionDetailsSummary = mutation.region_details_summary.overall;
}
}

wasCleared(): boolean {
if (this.regionDetailsSummary && this.regionDetailsSummary.clear_communicated_and_success) {
return true;
}
return false;
}
}

0 comments on commit 1164142

Please sign in to comment.