<!DOCTYPE html>
<html lang="en">
  <body>
    <style>
      @keyframes spin {
        to { transform: translate(calc(-50%), calc(50vh - 50%)) rotate(360deg); }
      }
      .mermaidTooltip {
        display: none;
      }
      .wrapper {
        display: flex;
      }
      .mermaid {
        width: 60%;
      }
      .pane-loading {
        font-size: 0;
        color: transparent;
      }
      .pane-loading::after {
        content: "";
        position: absolute;
        inset: 0 50%;
        width: 32px;
        height: 32px;
        transform: translate(calc(-50%), calc(50vh - 50%));
        border: 0.25rem solid #9370DB;
        border-bottom: 0.25rem solid rgba(0,0,0,0);
        border-radius: 50%;
        animation: spin 1s linear infinite;
      }
      #editor {
        font-size: 13px;
        width: 40%;
        position: relative;
      }
      font {
        font-size: 24px;
        padding: 24px;
      }
      .node:hover {
        stroke: #2272B4 !important;
        color: #0E538B !important;
        background-color: #bbdaf4;
      }
    </style>

    <div class="wrapper">
      <div class="mermaid pane-loading">
          flowchart TD

          subgraph " "
            recipe([recipe.yaml])

            ingestUserCode([steps/ingest.py]) --> ingestMLPStep[["<font>ingest</font>"]]
            ingestMLPStep --> dataParquet[(ingested_data)]

            ingestScoringUserCode([steps/ingest.py]) --> ingestScoringMLPStep[["<font>ingest_scoring</font>"]]
            ingestScoringMLPStep --> dataScoringParquet[(ingested_scoring_data)]

            dataScoring[(ingested_scoring_data)] --> predictMLPStep[["<font>predict</font>"]]
            predictMLPStep --> dataScored[(scored_data)]
          end

          data[(ingested_data)] --> splitStep[["<font>split</font>"]]
          transformUserCode([steps/transform.py]) --> transformMLPStep
          splitUserCode([steps/split.py]) --> splitStep
          splitStep --> splitData0[(training_data)]
          splitStep --> splitData1[(validation_data)]
          splitStep --> splitData2[(test_data)]
          splitData0 --> transformMLPStep[["<font>transform</font>"]]
          splitData1 --> transformMLPStep[["<font>transform</font>"]]

          transformMLPStep --> transformedParquet[(transformed_training_data, <br/> transformed_validation_data)]
          transformMLPStep --> transformer{{transformer}}
          transformedParquet --> trainMLPStep[["<font>train</font>"]]

          trainUserCode([steps/train.py]) --> trainMLPStep
          customMetricsUserCode([steps/custom_metrics.py]) --> trainMLPStep
          transformer --> trainMLPStep
          trainMLPStep --> run{{run}}
          trainMLPStep --> model{{model}}
          trainMLPStep --> predictedTrainingData[(predicted_training_data)]

          model --> evaluateMLPStep[["<font>evaluate</font>"]]
          splitData1 --> evaluateMLPStep
          splitData2 --> evaluateMLPStep
          customMetricsUserCode --> evaluateMLPStep
          evaluateMLPStep --> model_validation_status{{model_validation_status}}
          run --> registerMLPStep[["<font>register</font>"]]
          run --> evaluateMLPStep

          model_validation_status --> registerMLPStep
          registerMLPStep --> registered_model_version{{registered_model_version}}

          click ingestMLPStep renderMoreInformation "{{ingest_step_help}}"
          click ingestUserCode renderMoreInformation "{{ingest_user_code_help}}"
          click splitStep renderMoreInformation "{{split_step_help}}"
          click transformMLPStep renderMoreInformation "{{transform_step_help}}"
          click transformUserCode renderMoreInformation "{{transform_user_code_help}}"
          click trainMLPStep renderMoreInformation "{{train_step_help}}"
          click trainUserCode renderMoreInformation "{{train_user_code_help}}"
          click evaluateMLPStep renderMoreInformation "{{evaluate_step_help}}"
          click registerMLPStep renderMoreInformation "{{register_step_help}}"
          click customMetricsUserCode renderMoreInformation "{{custom_metrics_user_code_help}}"
          click splitUserCode renderMoreInformation "{{split_user_code_help}}"
          click ingestScoringUserCode renderMoreInformation "{{ingest_user_code_help}}"
          click ingestScoringMLPStep renderMoreInformation "{{ingest_scoring_step_help}}"
          click predictMLPStep renderMoreInformation "{{predict_step_help}}"

          click recipe renderMoreInformation "{{recipe_yaml_help}}"
          click dataParquet renderMoreInformation "{{ingested_data_help}}"
          click data renderMoreInformation "{{ingested_data_help}}"
          click splitData0 renderMoreInformation "{{training_data_help}}"
          click splitData1 renderMoreInformation "{{validation_data_help}}"
          click splitData2 renderMoreInformation "{{test_data_help}}"
          click transformedParquet renderMoreInformation "{{transformed_training_and_validation_data_help}}"
          click run renderMoreInformation "{{mlflow_run_help}}"
          click model renderMoreInformation "{{fitted_model_help}}"
          click predictedTrainingData renderMoreInformation "{{predicted_training_data_help}}"
          click transformer renderMoreInformation "{{fitted_transformer_help}}"
          click model_validation_status renderMoreInformation "{{model_validation_status_help}}"
          click registered_model_version renderMoreInformation "{{registered_model_version_help}}"
          click dataScoringParquet renderMoreInformation "{{ingested_scoring_data_help}}"
          click dataScoring renderMoreInformation "{{ingested_scoring_data_help}}"
          click dataScored renderMoreInformation "{{scored_data_help}}"

      </div>
      <div id="editor"></div>
    </div>

    <script src="https://requirejs.org/docs/release/2.1.5/comments/require.js"></script>
    <script type="text/javascript">
      require.config({
        paths: {
            "mermaid": "https://cdn.jsdelivr.net/npm/mermaid@9.3.0/dist/mermaid.min",
            "ace": "https://cdnjs.cloudflare.com/ajax/libs/ace/1.5.1/",
            "python": "https://cdnjs.cloudflare.com/ajax/libs/ace/1.5.1/mode-python.min",
            "yaml": "https://cdnjs.cloudflare.com/ajax/libs/ace/1.5.1/mode-yaml.min",
            "idle_fingers": "https://cdnjs.cloudflare.com/ajax/libs/ace/1.5.1/theme-idle_fingers.min",
        },
      });
      require(["mermaid"],
        function (mermaid) {
          const config = {
            startOnLoad:true,
            securityLevel:'loose',
            theme: (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) ? "dark" : "default",
            themeCSS: ".cluster rect { fill: none; stroke: none; }",
            flowchart:{
                useMaxWidth:true,
                htmlLabels:true,
            }
          };
          document.querySelector('.mermaid.pane-loading').classList.remove('pane-loading');
          mermaid.initialize(config);
          mermaid.init();
          const nodes = document.querySelectorAll(".node");
          [...nodes].forEach((node) => {
            var nodeInfo = node.getAttribute('title');
            // In order to parse the node text as JSON with `help_string` and `help_string_type`
            // attributes, single quotes must be replaced with double quotes. However, we also want
            // to convert backslashed single quotes to single quotes in the parsed JSON and preserve
            // backslashed double quotes in the parsed JSON. Accordingly, we first replace instances
            // of the string \' with \\" . Then, we replace all instances of ' with " . Finally, we
            // replace instances of \\" with '
            nodeInfo = nodeInfo && nodeInfo.replace(/\\'/g, '\\\\"');
            nodeInfo = nodeInfo && nodeInfo.replace(/'/g, '"');
            nodeInfo = nodeInfo && nodeInfo.replace(/\\\\"/g, "'");
            if (nodeInfo) {
              const nodeLabel = node.querySelector(".nodeLabel");
              nodeLabel.setAttribute("title", JSON.parse(nodeInfo).help_string)
            }
          })
          resetStyles()
          window.editor = null;
        }
      );

      var resetStyles = function() {
        const allNodes = document.querySelector('.nodes').querySelectorAll('[id^="flowchart-"]');
          [...allNodes].forEach(node => {
            const rect = node.firstChild
            if (rect) {
              rect.style.fill = "inherit";
              rect.style.stroke = "inherit";
            }
            node.style.fill = "#F2F5F7";
            node.style.stroke = "#CDDAE5";
            node.style.color = "#20272E";
            const span = node.querySelector('span')
            const label = node.querySelector('.label')
            span.style.color = "inherit";
            label.style.color = "inherit";
          });
      }
      var noOp = function() {}
      var renderMoreInformation = function(nodeId) {
        resetStyles()
        regex = `[id^="flowchart-${nodeId}-"]`
        const node = document.querySelector(regex);
        var nodeInfo = node.getAttribute('title');
        // In order to parse the node text as JSON with `help_string` and `help_string_type`
        // attributes, single quotes must be replaced with double quotes. However, we also want
        // to convert backslashed single quotes to single quotes in the parsed JSON and preserve
        // backslashed double quotes in the parsed JSON. Accordingly, we first replace instances
        // of the string \' with \\" . Then, we replace all instances of ' with " . Finally, we
        // replace instances of \\" with '
        nodeInfo = nodeInfo && nodeInfo.replace(/\\'/g, '\\\\"');
        nodeInfo = nodeInfo && nodeInfo.replace(/'/g, '"');
        nodeInfo = nodeInfo && nodeInfo.replace(/\\\\"/g, "'");
        nodeInfo = JSON.parse(nodeInfo)

        const rect = node.firstChild;
        rect.style.stroke = "#04355D";
        rect.style.fill = "#bbdaf4";
        rect.style.color = "#04355D";

        document.querySelector("#editor").classList.add('pane-loading');
        const hideSpinner = () => document.querySelector("#editor").classList.remove('pane-loading');

        require(["ace/ace"],
          function (ace) {
            require(["python", "yaml", "idle_fingers"],
              function () {
                hideSpinner();
                if (!window.editor) {
                  const editor = ace.edit("editor");
                  editor.setTheme("ace/theme/idle_fingers");
                  // Wrap text lines for readability
                  editor.session.setUseWrapMode(true);
                  // Minimize indentation on wrapped lines by setting tab size to 1 character
                  // (note that 0 characters does not appear to work. TODO: Find a better approach)
                  editor.setOption("tabSize", 1);
                  editor.setReadOnly(true);
                  // Disable active line highlighting
                  editor.setHighlightActiveLine(false);
                  // Hide the cursor, the presence of which creates the misconception
                  // that the text is editable
                  editor.renderer.$cursorLayer.element.style.display = "none"
                  // Hide the editor gutter, since the window isn't really an editor and instead
                  // is intended for displaying text
                  editor.renderer.setShowGutter(false);
                  // Disable the line length ruler
                  editor.setShowPrintMargin(false);
                  window.editor = editor;
                }
                // Set the editor value and the editor session value to the text associated with
                // the recipe node; setting the session is important in order to avoid
                // obtrusive highlighting
                window.editor.setValue(nodeInfo.help_string)
                window.editor.session.setValue(nodeInfo.help_string)
                window.editor.session.setMode(`ace/mode/${nodeInfo.help_string_type}`);
              }, hideSpinner
            );
          }, hideSpinner
        );
      }
    </script>

  </body>
</html>
