modules/pre/reporter_job_control.mjs

// @ts-ignore
import { gui } from "./reporter_job_control_gui.jsi";
import { ReporterVariablesFile } from "../shared/reporter_variables_transfer.mjs";
import { find_lsdyna_files } from "../shared/file_helper.mjs";

/**
 * GUI to allow user to specify keyword file, results directory and output directory for
 * REPORTER template "job".
 * @param {string} keyword_file Keyword file
 * @param {string} results_dir Directory containing LS-DYNA results (can be different from keyword file directory)
 * @param {string} output_dir Directory where images and other output files will be written
 * @param {string} reporter_temp REPORTER's temporary directory, used for exchanging REPORTER variables
 * @param {string} job_control Overall job control
 */
export function reporter_job_control(keyword_file, results_dir, output_dir, reporter_temp, job_control) {
    //@ts-ignore
    if (!JobControl.GetAll().includes(job_control)) {
        ErrorMessage(`Unexpected value of <job_control = "${job_control}"> in function reporter_job_control.`);
        Exit();
    }
    if (job_control == JobControl.ABORT) {
        Message(`Generation aborted (job control).`);
        return;
    }
    if (gui) {
        /* Set callback functions here (we can't set them in GUI builder because it doesn't like
         * when they're defined when gui is imported from within a module). */
        gui.w.T_keyword_file.onChange = check_valid;
        gui.w.T_results_dir.onChange = check_valid;
        gui.w.T_output_dir.onChange = check_valid;
        gui.w.B_keyword_file.onClick = browse_keyword_file;
        gui.w.B_results_dir.onClick = browse_results_dir;
        gui.w.B_output_dir.onClick = browse_output_dir;
        gui.w.B_run.onClick = run_clicked;
        gui.w.B_cancel.onClick = cancel_clicked;

        /* Browse icons */
        gui.w.B_keyword_file.DirectoryIcon(Widget.BLACK, Widget.YELLOW);
        gui.w.B_results_dir.DirectoryIcon(Widget.BLACK, Widget.YELLOW);
        gui.w.B_output_dir.DirectoryIcon(Widget.BLACK, Widget.YELLOW);

        /* Populate textboxes */
        gui.w.T_keyword_file.text = keyword_file;
        gui.w.T_results_dir.text = results_dir;
        gui.w.T_output_dir.text = output_dir;

        /* Add reporter_temp directory to <gui> object to make it accessible in callback functions. */
        if (!File.IsDirectory(reporter_temp)) {
            ErrorMessage(`Invalid REPORTER temporary directory for file exchange: ${reporter_temp}`);
            Exit();
        }
        gui._reporter_temp = reporter_temp;

        /* Check whether current selections are valid */
        check_valid();

        gui.w.Show();
    } else {
        ErrorMessage(`Unable to open job control GUI.`);
        Exit();
    }
}

/**
 * Checks whether the textbox entries for KEYWORD_FILE, RESULTS_DIR, and OUTPUT_DIR are valid.
 * Controls whether `Run` button is active.
 */
function check_valid() {
    let all_valid = true;
    if (File.Exists(gui.w.T_keyword_file.text)) {
        gui.w.T_keyword_file.category = Widget.CATEGORY_TEXT_BOX;
    } else {
        gui.w.T_keyword_file.category = Widget.CATEGORY_WARNING_ACTION;
        all_valid = false;
    }
    if (File.IsDirectory(gui.w.T_results_dir.text)) {
        gui.w.T_results_dir.category = Widget.CATEGORY_TEXT_BOX;
    } else {
        gui.w.T_results_dir.category = Widget.CATEGORY_WARNING_ACTION;
        all_valid = false;
    }
    if (File.IsDirectory(gui.w.T_output_dir.text)) {
        gui.w.T_output_dir.category = Widget.CATEGORY_TEXT_BOX;
    } else {
        gui.w.T_output_dir.category = Widget.CATEGORY_WARNING_ACTION;
        all_valid = false;
    }
    if (all_valid) {
        gui.w.B_run.active = true;
    } else {
        gui.w.B_run.active = false;
    }
    /* Redraw window to force widget category changes to refresh */
    gui.w.Redraw();
}

/**
 * Allows user to browse for a keyword file
 */
function browse_keyword_file() {
    /* If there is already a keyword file entry, use its location as the starting point for the
     * file selector. */
    let path_index = Math.max(gui.w.T_keyword_file.text.lastIndexOf("/"), gui.w.T_keyword_file.text.lastIndexOf("\\"));
    let start_dir = gui.w.T_keyword_file.text.substring(0, path_index);
    let keyword_file;
    if (File.IsDirectory(start_dir)) {
        keyword_file = Window.GetFile(`*.k*`, false, start_dir);
    } else {
        keyword_file = Window.GetFile(`*.k*`);
    }
    if (keyword_file != null) {
        gui.w.T_keyword_file.text = keyword_file;
    }
    check_valid();
}

/**
 * Allows user to browse for a results directory
 */
function browse_results_dir() {
    browse_dir(gui.w.T_results_dir);
}

/**
 * Allows user to browse for an output directory
 */
function browse_output_dir() {
    browse_dir(gui.w.T_output_dir);
}

/**
 * Allows the user to browse for a directory. Updates the appropriate textbox.
 * @param {Widget} textbox The directory textbox associated with this browse button
 */
function browse_dir(textbox) {
    /* If the textbox already contains a valid directory, use it as the starting point for the
     * directory selector. */
    let new_dir;
    if (File.IsDirectory(textbox.text)) {
        new_dir = Window.GetDirectory(textbox.text);
    } else {
        new_dir = Window.GetDirectory();
    }
    if (new_dir != null) {
        textbox.text = new_dir;
    }
    check_valid();
}

/**
 * When user clicks `Run`, write KEYWORD_FILE, RESULTS_DIR, and OUTPUT_DIR to a temporary CSV
 * file that can be picked up by REPORTER in the subsequent `#AAW REPORTER read variables from
 * PRIMER job control` item.
 * Set JOB_CONTROL to "Check", ready for `#AAW T/HIS check and do assessment` item.
 */
function run_clicked() {
    let keyword_file = gui.w.T_keyword_file.text;
    let results_dir = gui.w.T_results_dir.text;
    let output_dir = gui.w.T_output_dir.text;

    /* Determine RESULTS_FILE_THIS */
    let path_index = Math.max(keyword_file.lastIndexOf("/"), keyword_file.lastIndexOf("\\"));
    let filename = keyword_file.substring(path_index + 1);
    let job_name = filename.substring(0, filename.lastIndexOf("."));
    let results_file_this = find_lsdyna_files(results_dir, job_name, "T/HIS");

    /* Write variables file */
    let file = new ReporterVariablesFile(gui._reporter_temp, ReporterVariablesFile.JOB_CONTROL);
    file.WriteVariable(`KEYWORD_FILE`, keyword_file, `Keyword file`, `File(absolute)`);
    file.WriteVariable(`RESULTS_DIR`, results_dir, `Directory containing LS-DYNA results`, `Directory`);
    file.WriteVariable(`OUTPUT_DIR`, output_dir, `Directory for output files`, `Directory`);
    file.WriteVariable(`RESULTS_FILE_THIS`, results_file_this, `T/HIS results filename`, `String`);
    file.WriteVariable(`JOB_CONTROL`, JobControl.CHECK, `"Check"/"Run"/"Skip"/"Abort" for #AAW items`, `String`);
    file.Close();
    Exit();
}

/**
 * If user clicks `Cancel`, set JOB_CONTROL to "Abort" so that template generation is aborted ASAP.
 */
function cancel_clicked() {
    let file = new ReporterVariablesFile(gui._reporter_temp, ReporterVariablesFile.JOB_CONTROL);
    /* We are aborting template generation so just write a blank filename for results file: */
    file.WriteVariable(`RESULTS_FILE_THIS`, ``, `T/HIS results filename`, `String`);
    file.WriteVariable(`JOB_CONTROL`, JobControl.ABORT, `"Check"/"Run"/"Skip"/"Abort" for #AAW items`, `String`);
    file.Close();
    Exit();
}

export class JobControl {
    /**
     * This class holds constants used for REPORTER job control i.e. controlling whether various
     * items in REPORTER templates are run or skipped, or whether the entire template generation
     * is aborted.
     */

    /** Abort constant (used to indicate that the entire template generation should be aborted)
     * @type {string} */
    static get ABORT() {
        return "Abort";
    }

    /** Check constant (used to indicate that the check in this item should be performed)
     * @type {string} */
    static get CHECK() {
        return "Check";
    }

    /** Run constant (used to indicate that the item should be run)
     * @type {string} */
    static get RUN() {
        return "Run";
    }

    /** Skip constant (used to indicate that the item should be skipped)
     * @type {string} */
    static get SKIP() {
        return "Skip";
    }

    /* Class methods */

    /**
     * Return an array of all the available job control constants
     * @returns {string[]}
     * @example
     * let job_controls = JobControl.GetAll();
     */
    static GetAll() {
        return [JobControl.ABORT, JobControl.CHECK, JobControl.RUN, JobControl.SKIP];
    }
}