tutorial step 1
unknown
typescript
3 years ago
10 kB
11
Indexable
import filter from "lodash/filter";
import { Vue3Instance } from "vue/types/v3-component-public-instance";
import { SearchEntityType } from "@/proto/search/search_pb";
import { axii } from "@/assets/icons/icons-svg";
import { findDrawflowNodeByName, waitForElement } from "@/tutorial/index";
//@ts-ignore
export const createDraftSteps = async (context: Vue3Instance) => {
const svg = document.createElement("div");
svg.classList.add("svg__wrapper");
svg.innerHTML = axii;
const paragraph = document.createElement("p");
paragraph.classList.add("content");
paragraph.innerText =
"Welcome to Part 1 of AXII Tutorial. In this tutorial we show you the basics of the application: \
how to build and execute a simple ML workflow.";
const wrapper = document.createElement("div");
wrapper.appendChild(svg);
wrapper.appendChild(paragraph);
const nodesMap: { iris: string | null; statistics: string | null } = {
iris: null,
statistics: null,
};
const steps = [
{
canClickTarget: false,
text: wrapper,
classes: "welcome-step",
buttons: [context.buttonStart],
},
{
attachTo: { element: "#drafts-link", on: "right" },
text: "We create the workflow in the Drafts view. Please click here to proceed.",
modalOverlayOpeningPadding: 10,
advanceOn: { selector: "#drafts-link", event: "click" },
},
{
text: "A Draft is a workflow in the 'work in progress' stage.<br /><br />Click on this button to \
start composing your first Draft.",
attachTo: { element: "#add-new-draft__btn", on: "left" },
modalOverlayOpeningPadding: 10,
advanceOn: { selector: "#add-new-draft__btn", event: "click" },
beforeShowPromise: () => {
return new Promise((resolve) => context.resolveIfMounted(resolve, "Drafts"));
},
},
{
text: "The Draft Editor lets you build workflows in a graphical way.<br /><br />We begin with adding \
a DataSource, which is a static piece of data (e.g. dataset). In this tutorial we use \
the Iris dataset.",
beforeShowPromise: () => {
return new Promise((resolve) => context.resolveIfMounted(resolve, "Draft"));
},
buttons: [context.buttonNext],
},
{
attachTo: {
element: "#draft-editor",
on: "top",
},
text: "To add it, right click anywhere on the canvas and then click <b>Add DataSource</b>",
beforeShowPromise: () => {
return new Promise((resolve) => waitForElement("#draft-editor", () => resolve(true)));
},
when: {
show: () => {
waitForElement(
`#context-menu-search-bar \
[class~=search-bar-entity-type__${SearchEntityType.SEARCH_ENTITY_TYPE_DATA_SOURCE}]`,
() => context.tour.next()
);
},
},
},
{
attachTo: {
element: "#draft-editor",
on: "top",
},
text: "You should see a search bar and some results. Please start typing <b>Iris</b> \
to find the right one now (it is named <b>Iris Data Set</b>). Click on it when you see it.",
beforeShowPromise: () => {
return new Promise((resolve) => {
waitForElement("div[class=search-bar__wrapper]", () => resolve(true));
});
},
when: {
show: () => {
const interval = setInterval(() => {
const searchedNode = findDrawflowNodeByName(/^Iris Data Set.*/i);
if (searchedNode) {
nodesMap.iris = (searchedNode as HTMLElement).id;
context.tour.next();
clearInterval(interval);
}
}, 1000);
console.debug("base.ts, show", document.activeElement);
},
},
},
{
attachTo: {
element: () => document.querySelector(`#${nodesMap.iris}`),
on: "right",
},
text: "A block representing the DataSource appeared on the canvas.",
modalOverlayOpeningPadding: 10,
buttons: [context.buttonNext],
},
{
attachTo: {
element: "#draft-editor",
on: "top",
},
text: "Next, we add a Task block. Task is something that is executed to process the data.<br /><br />\
Similarly as before, right click on the canvas and then on <b>Add Task</b>.",
when: {
show: () => {
const interval = setInterval(() => {
const addDataSourceBtn = filter(document.querySelectorAll(".v-list-item__content"), (node) => {
return !!node?.textContent?.includes("Add Task");
});
if (addDataSourceBtn.length > 0) {
addDataSourceBtn[0].addEventListener("click", () => {
context.tour.next();
});
clearInterval(interval);
}
}, 1000);
},
},
},
{
attachTo: {
element: "#draft-editor",
on: "top",
},
text: "Search for <b>Dataset Statistics</b> Task and click on it when you see it.",
when: {
show: () => {
const interval = setInterval(() => {
const searchedNode = findDrawflowNodeByName(/^Dataset Statistics.*/i);
if (searchedNode) {
nodesMap.statistics = (searchedNode as HTMLElement).id;
context.tour.next();
clearInterval(interval);
}
}, 1000);
},
},
},
{
attachTo: {
element: () => document.querySelector(`#${nodesMap.statistics}`),
on: "right",
},
text: "The Task block appeared on the canvas.<br /><br />We now connect this two blocks to define \
how the data flows between them.",
buttons: [context.buttonNext],
},
{
attachTo: {
element: "#draft-editor",
on: "top",
},
text: "Blocks might have some number of Inputs and Outputs. They are represented by semicircles on the \
edges of the block.<br /><br />Click on the Iris Data Set's output (right edge) and drag it to \
the Dataset Statistics' input (left edge) to create the connection.",
when: {
show: () => {
waitForElement(`.connection.node_out_${nodesMap.iris}.node_in_${nodesMap.statistics}`, () =>
context.tour.next()
);
},
},
},
{
attachTo: {
element: () => document.querySelector("#draft-name__input"),
on: "bottom",
},
modalOverlayOpeningPadding: 10,
text: "The Draft is almost ready to be executed. Let's give it a meaningful name: <b>Hello AXII</b>.",
when: {
show: () => {
waitForElement(
"#draft-name__input",
() => context.tour.next(),
(el: Element): boolean => {
return Boolean((el as HTMLInputElement).value.match(/.*Hello AXII.*/i));
}
);
},
},
},
{
attachTo: {
element: "#submit-btn",
on: "left",
},
text: "Good to go. The execution of a Draft is called an Experiment. \
Click on this button to submit it for execution.",
modalOverlayOpeningPadding: 10,
when: {
show: () => {
const submitBtn = document.querySelector("#submit-btn");
if (submitBtn) {
submitBtn.addEventListener("click", () => {
const interval = setInterval(() => {
const id = document.querySelector(".v-snack__content a")?.textContent;
if (id) {
clearInterval(interval);
context.$router.push(`/experiment/${id}`);
context.tour.next();
}
}, 1000);
});
}
},
},
},
{
attachTo: {
element: "#experiment-state",
on: "right",
},
text: "Let's wait untill the Experiment is in <b>Done</b> state.",
beforeShowPromise: () => {
return new Promise((resolve) => {
context.resolveIfMounted(resolve, "Experiment");
});
},
when: {
show: () => {
waitForElement(
"#experiment-state",
() => context.tour.next(),
(el: Element) => {
return el.textContent == "Done";
}
);
},
},
},
{
attachTo: {
element: () => document.querySelector(`#${nodesMap.statistics}`),
on: "top",
},
text: "When the Experiment is finished, we can inspect what outputs (Artifacts) the Task produced.<br /><br />\
Click on the Dataset Statistics node to inspect it.",
when: {
show: () => {
waitForElement("#node-status-viewer", () => context.tour.next());
},
},
},
{
attachTo: {
element: () => document.querySelector("#node-status-viewer"),
on: "left",
},
text: "Node preview appeared on the right. You can find there some details about the executed Task and \
the list of generated Artifacts.",
buttons: [context.buttonNext],
},
{
attachTo: {
element: "#preview-data-btn",
on: "top",
},
text: "Click on the Preview button to peek inside the <b>stats_csv</b> artifact.",
advanceOn: {
selector: "#preview-data-btn",
event: "click",
},
},
{
attachTo: {
element: "#preview-dialog",
on: "top",
},
text: "This is the preview of the CSV file with computed statistics.",
buttons: [context.buttonNext],
},
{
text: "Congratulations, you have just completed Part 1 of the tutorial. You can now explore AXII on \
your own or move on to Part 2, where we show you how you can find and reuse whole Experiments.",
buttons: [context.buttonFinish, context.buttonNextTutorial],
when: {
show: () => {
localStorage.setItem(context.tutorialStorageKey, "true");
},
},
},
];
return steps;
};
Editor is loading...