ui/controllers/job_run_controller.js

const $ = require('jquery');
const { BagItProfile } = require('../../bagit/bagit_profile');
const { Constants } = require('../../core/constants');
const { Context } = require('../../core/context');
const { DartProcess } = require('../../core/dart_process');
const { fork } = require('child_process');
const fs = require('fs');
const { Job } = require('../../core/job');
const { JobRunner } = require('../../workers/job_runner');
const path = require('path');
const { RunningJobsController } = require('./running_jobs_controller');
const Templates = require('../common/templates');
const { UIConstants } = require('../common/ui_constants');
const { StorageService } = require('../../core/storage_service');
const { Util } = require('../../core/util');

/**
 * The JobRunController displays the page where users review
 * and run a Job.
 *
 * @param {URLSearchParams} params - The URL search params parsed
 * from the URL used to reach this page. This should contain at
 * least the Job Id.
 *
 * @param {string} params.id - The id of the Job being worked
 * on. Job.id is a UUID string.
 */
class JobRunController extends RunningJobsController {

    constructor(params) {
        super(params, 'Jobs');
        this.model = Job;
        this.job = Job.find(this.params.get('id'));
        this.dartProcess = null;
        this.childProcess = null;
        this.reachedEndOfOutput = false;
    }

    /**
     * Displays a summary of the Job and the "Run Job" button.
     */
    show() {
        let uploadTargets = [];
        for (let op of this.job.uploadOps) {
            let target = StorageService.find(op.storageServiceId);
            if (target) {
                uploadTargets.push(target.name);
            }
        }
        let data = {
            job: this.job,
            uploadTargets: uploadTargets,
            trimPath: this._getTrimPath()
        }
        return this.containerContent(Templates.jobRun(data));
    }

    /**
     * Runs the Job in a separate process.
     */
    run() {
        if (!this._checkOutputPath()) {
            return this.noContent()
        }
        let proc = Util.forkJobProcess(this.job);
        this.childProcess = proc.childProcess;
        this.dartProcess = proc.dartProcess;
        this.initRunningJobDisplay(this.dartProcess);
        Context.childProcesses[this.dartProcess.id] = this.dartProcess;
        $('#btnRunJob').prop('disabled', true);
        return this.noContent();
    }

    /**
     * This handles the page's Back button click.
     */
    back() {
        let prevController = 'JobUpload';
        // If we have a workflow id, the uploads for this job
        // are already determined, so we can skip that screen.
        if (this.job.workflowId) {
            prevController = 'JobMetadata';
        }
        return this.redirect(prevController, 'show', this.params);
    }

    /**
     * If the output path is a non-empty directory, prompt the user to
     * delete it. We call this before running a job.
     */
    _checkOutputPath() {
        let okToRunJob = true
        if (this.job.packageOp && Util.isNonEmptyDirectory(this.job.packageOp.outputPath)) {
            let okToDelete = confirm(Context.y18n.__("You must delete the non-empty directory already at %s before running this job. Click OK to delete it or Cancel to stop.", this.job.packageOp.outputPath))
            if (okToDelete) {
                // Note that this will throw an exception if outputPath is
                // something like "/" or "C:\Users"
                Util.deleteRecursive(this.job.packageOp.outputPath)
            } else {
                okToRunJob = false
            }
        }
        return okToRunJob
    }

    _getTrimPath() {
        let trimPath = '';
        if (this.job.packageOp && this.job.packageOp.trimLeadingPaths()) {
            trimPath = Util.findCommonPathPrefix(this.job.packageOp.sourceFiles);
        }
        return trimPath;
    }

    postRenderCallback(fnName) {

    }
}

module.exports.JobRunController = JobRunController;