tutorial step 1

 avatar
unknown
typescript
3 years ago
10 kB
6
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...