<commit_before>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

describe('openlmis.requisitions.requisition state', function() {

    var $state, $stateParams, $rootScope, $q,
        currentUserService, requisitionService, requisitionViewFactory,
        UserDataBuilder, RequisitionDataBuilder,
        user, state, requisition;

    beforeEach(function() {
        loadModules();
        injectServices();
        prepareTestData();
        prepareSpies();

        state = $state.get('openlmis.requisitions.requisition');
    });

    it('should fetch user', function() {
        var result;

        state.resolve.user(currentUserService).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(user);
    });

    it('should fetch requisition', function() {
        var result;

        state.resolve.requisition($stateParams, requisitionService).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(requisition);
    });

    it('should resolve if user has right to submit', function() {
        var result;

        state.resolve.canSubmit(requisitionViewFactory, user, requisition).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if user has right to authorize', function() {
        var result;

        state.resolve.canAuthorize(requisitionViewFactory, user, requisition).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if user has right to approve or reject', function() {
        var result;

        state.resolve.canApproveAndReject(requisitionViewFactory, user, requisition).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if user has right to delete', function() {
        var result;

        state.resolve.canDelete(requisitionViewFactory, user, requisition).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if user has right to skip', function() {
        var result;

        state.resolve.canSkip(requisitionViewFactory, user, requisition).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if user has right to sync', function() {
        var result;

        state.resolve.canSync(requisitionViewFactory, user, requisition).then(function(response) {
            result = response;
        });

        $rootScope.$apply();

        expect(result).toBe(true);
    });

    function loadModules() {
        module('openlmis-main-state');
        module('requisition');
        module('requisition-view');
    }

    function injectServices() {
        inject(function($injector) {
            $state = $injector.get('$state');
            $rootScope = $injector.get('$rootScope');
            $q = $injector.get('$q');

            currentUserService = $injector.get('currentUserService');
            requisitionViewFactory = $injector.get('requisitionViewFactory');
            requisitionService = $injector.get('requisitionService');

            UserDataBuilder = $injector.get('UserDataBuilder');
            RequisitionDataBuilder = $injector.get('RequisitionDataBuilder');
        });
    }

    function prepareTestData() {
        user = new UserDataBuilder().build();
        $stateParams = {};
        requisition = new RequisitionDataBuilder().build();
    }

    function prepareSpies() {
        spyOn(currentUserService, 'getUserInfo').andReturn($q.resolve(user));

        spyOn(requisitionService, 'get').andReturn($q.resolve(requisition));

        spyOn(requisitionViewFactory, 'canSubmit').andReturn($q.resolve(true));
        spyOn(requisitionViewFactory, 'canAuthorize').andReturn($q.resolve(true));
        spyOn(requisitionViewFactory, 'canApproveAndReject').andReturn($q.resolve(true));
        spyOn(requisitionViewFactory, 'canDelete').andReturn($q.resolve(true));
        spyOn(requisitionViewFactory, 'canSkip').andReturn($q.resolve(true));
        spyOn(requisitionViewFactory, 'canSync').andReturn($q.resolve(true));
    }

});<commit_msg>OLMIS-5724: Refactored requisition-view.routes.spec.js to not use any defined-scope variables<commit_after>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

describe('openlmis.requisitions.requisition state', function() {

    beforeEach(function() {
        module('requisition-view');

        var $state, UserDataBuilder, RequisitionDataBuilder;
        inject(function($injector) {
            UserDataBuilder = $injector.get('UserDataBuilder');
            RequisitionDataBuilder = $injector.get('RequisitionDataBuilder');
            $state = $injector.get('$state');

            this.$rootScope = $injector.get('$rootScope');
            this.$q = $injector.get('$q');
            this.currentUserService = $injector.get('currentUserService');
            this.requisitionViewFactory = $injector.get('requisitionViewFactory');
            this.requisitionService = $injector.get('requisitionService');
        });

        this.user = new UserDataBuilder().build();
        this.$stateParams = {};
        this.requisition = new RequisitionDataBuilder().build();

        spyOn(this.currentUserService, 'getUserInfo').andReturn(this.$q.resolve(this.user));
        spyOn(this.requisitionService, 'get').andReturn(this.$q.resolve(this.requisition));
        spyOn(this.requisitionViewFactory, 'canSubmit').andReturn(this.$q.resolve(true));
        spyOn(this.requisitionViewFactory, 'canAuthorize').andReturn(this.$q.resolve(true));
        spyOn(this.requisitionViewFactory, 'canApproveAndReject').andReturn(this.$q.resolve(true));
        spyOn(this.requisitionViewFactory, 'canDelete').andReturn(this.$q.resolve(true));
        spyOn(this.requisitionViewFactory, 'canSkip').andReturn(this.$q.resolve(true));
        spyOn(this.requisitionViewFactory, 'canSync').andReturn(this.$q.resolve(true));

        this.state = $state.get('openlmis.requisitions.requisition');
    });

    it('should fetch this.user', function() {
        var result;

        this.state.resolve.user(this.currentUserService).then(function(response) {
            result = response;
        });

        this.$rootScope.$apply();

        expect(result).toBe(this.user);
    });

    it('should fetch this.requisition', function() {
        var result;

        this.state.resolve.requisition(this.$stateParams, this.requisitionService).then(function(response) {
            result = response;
        });

        this.$rootScope.$apply();

        expect(result).toBe(this.requisition);
    });

    it('should resolve if this.user has right to submit', function() {
        var result;

        this.state.resolve.canSubmit(this.requisitionViewFactory, this.user, this.requisition).then(function(response) {
            result = response;
        });

        this.$rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if this.user has right to authorize', function() {
        var result;

        this.state.resolve.canAuthorize(this.requisitionViewFactory, this.user, this.requisition)
            .then(function(response) {
                result = response;
            });

        this.$rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if this.user has right to approve or reject', function() {
        var result;

        this.state.resolve.canApproveAndReject(this.requisitionViewFactory, this.user, this.requisition)
            .then(function(response) {
                result = response;
            });

        this.$rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if this.user has right to delete', function() {
        var result;

        this.state.resolve.canDelete(this.requisitionViewFactory, this.user, this.requisition)
            .then(function(response) {
                result = response;
            });

        this.$rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if this.user has right to skip', function() {
        var result;

        this.state.resolve.canSkip(this.requisitionViewFactory, this.user, this.requisition).then(function(response) {
            result = response;
        });

        this.$rootScope.$apply();

        expect(result).toBe(true);
    });

    it('should resolve if this.user has right to sync', function() {
        var result;

        this.state.resolve.canSync(this.requisitionViewFactory, this.user, this.requisition).then(function(response) {
            result = response;
        });

        this.$rootScope.$apply();

        expect(result).toBe(true);
    });

});<|endoftext|>


<commit_before>/**
 * workflow routes
 */
const Docker = require('dockerode');
const fs = require('fs-extended');
const path = require('path');
const Promise = require('bluebird');
const express = require('express');
const hash = require('object-hash');
const promiseRedis = require('promise-redis');
const isEmail = require('validator').isEmail;
const shortId = require('shortid');

const router = new express.Router();

const WORKFLOW_TEMP_FOLDER = '/tmp/workflow_downloads/';
const WORKFLOW_WORK_FOLDER = '/tmp/workflows/';
const INPUTS = 'inputs';
const OUTPUTS = 'outputs';

/* Redis constants */
const REDIS_WORKFLOWS = 'workflows';
const REDIS_RUNS = 'runs';
const REDIS_WORKFLOW_EMAIL_SET = 'workflow_emails';// redis<SET>
const REDIS_WORKFLOW_STATUS = 'workflow_status';// redis<HASH>
const REDIS_WORKFLOW_ERRORS = 'workflow_errors';// redis<HASH>

/* Docker image vars*/
const INPUT_FILE_NAME = 'input.json';
const OUTPUT_FILE_NAME = 'output.json';
const WORKFLOW_DOCKER_IMAGE = 'docker.io/busybox: latest';
const WORKFLOW_KEY = 'workflowId';

const WORKFLOW_STATE = {
  running: 'running',
  finished: 'finished',
  failed: 'failed',
  none: 'none',
};

const docker = new Docker({ socketPath: '/var/run/docker.sock' });

fs.ensureDirSync(WORKFLOW_TEMP_FOLDER);
fs.ensureDirSync(WORKFLOW_WORK_FOLDER);

const Redis = promiseRedis(resolver =>
  new Promise(resolver)
);
const redis = Redis.createClient({ host: 'localhost', port: 6379 });

/**
 * Hashes all files in a directory. If the files
 * are JSON, these will be hashed in a way where
 * semantically equivalent JSON objects will give
 * the same hash value.
 * This can be made async if needed.
 * @param  {[type]} path [description]
 * @return {[String]}      [description]
 */
function getWorkflowInputMd5(pathString) {
  const files = fs.listFilesSync(pathString, { recursive: true });
  files.sort();
  const hashes = [];
  files.forEach((e) => {
    const filePath = path.join(pathString, e);
    if (e.endsWith('.json')) {
      const data = fs.readFileSync(filePath, { encoding: 'utf8' });
      try {
        const jsobObj = JSON.parse(data);
        hashes.push(hash(jsobObj));
      } catch (err) {
        console.error(err);
        hashes.push(hash(data));
      }
    } else {
      const data = fs.readFileSync(filePath);
      hashes.push(hash(data));
    }
  });
  return hash(hashes);
}

/**
 * @return {[Promise]}
 */
function getDockerImage(dockerImage) {
  return new Promise((resolve, reject) => {
    docker.pull(dockerImage, (err, stream) => {
      if (err) {
        reject(err);
      } else {
        docker.modem.followProgress(stream, (finishedErr, output) => {
          // output is an array with output json parsed objects
          if (finishedErr) {
            reject(finishedErr);
          } else {
            console.log(output);
            resolve(true);
          }
        }, () => {});
      }
    });
  });
}

function getWorkflowPath(workflowId) {
  return path.join(WORKFLOW_WORK_FOLDER, workflowId);
}

function getWorkflowStdoutPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), 'stdout');
}

function getWorkflowStderrPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), 'stderr');
}

function getWorkflowExitCodePath(workflowId) {
  return path.join(getWorkflowPath(workflowId), 'exitCode');
}

function getWorkflowInputsPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), INPUTS);
}

function getWorkflowOutputsPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), OUTPUTS);
}

/**
 * @returns {[Promise]}
 */
function getDockerContainer(workflowId) {
  const labelKey = `${WORKFLOW_KEY}=${workflowId}`;
  const labelFilter = JSON.stringify({ label: [labelKey] });

  return new Promise((resolve, reject) => {
    docker.listContainers({ all: true, filters: labelFilter }, (err, containers) => {
      if (err) {
        reject(err);
      } else if (containers.length > 0) {
        resolve(containers[0]);
      } else {
        resolve(null);
      }
    });
  });
}

/**
 * @returns {[Promise]}
 */
function writeContainerLogs(workflowId, container, isStdOut) {
  const path = isStdOut ? getWorkflowStdoutPath(workflowId) : getWorkflowStderrPath(workflowId);
  const opts = {
    stdout: isStdOut ? 0 : 1,
    stderr: !isStdOut ? 0 : 1,
  };
  return new Promise((resolve, reject) => {
    container.logs(opts, (err, logstream) => {
      if (err) {
        reject(err);
      } else if (logstream !== null) {
        let logs = '';
        logstream.on('end', () => {
          fs.writeFileSync(path, logs);
          resolve();
        });
        logstream.on('data', (data) => {
          logs += logs + data;
        });
      } else {
        fs.writeFileSync(path, '');
        resolve();
      }
    });
  });
}

function setWorkflowState(workflowId, state) {
  return new Promise((resolve, reject) => {
    if (!WORKFLOW_STATE[state]) {
      reject(`Unknown workflow state=${state}`);
    } else {
      resolve(redis.hset(REDIS_WORKFLOW_STATUS, workflowId, state));
    }
  });
}

function sendEmailsWorkflowEnded() {
}

function processContainerEnd(workflowId) {
  const exitcodePath = getWorkflowExitCodePath(workflowId);
  getDockerContainer(workflowId)
    .then((containerData) => {
      const container = docker.getContainer(containerData.Id);
      container.wait((err, result) => {
        console.log(`workflow=${workflowId} in container=${containerData.Id} finished with exitCode=${result.StatusCode}`);
        fs.writeFileSync(exitcodePath, result.StatusCode);

        writeContainerLogs(workflowId, container, true)
          .then(() =>
            writeContainerLogs(workflowId, container, false)
          )
          .then(() => {
            // Finally set the state
            setWorkflowState(
              workflowId,
              result.StatusCode === 0 ?
                WORKFLOW_STATE.finished : WORKFLOW_STATE.failed
            );
            // Then remove the container
            container.remove({ force: 1 }, (errRemove) => {
              if (errRemove) {
                console.error(`Workflow=${workflowId} Error removing container=${containerData.Id} err]${errRemove}`);
              }
            });
            sendEmailsWorkflowEnded(workflowId);
          }).catch(console.error.bind(console));
      });
    }).catch(console.error.bind(console));
}

/**
 * [executeWorkflow description]
 * @param  {[type]} workflowDir [description]
 * @return {[Promise<Bool>]}             [description]
 */
function executeWorkflow(workflowId) {
  console.log(`executeWorkflow ${workflowId}`);
  getDockerImage(WORKFLOW_DOCKER_IMAGE)
    .then(() => {
      console.log('got image');
      return new Promise((resolve, reject) => {
        console.log(`executeWorkflow workflowId=${workflowId}`);

        const createOptions = { Image: WORKFLOW_DOCKER_IMAGE, WorkingDir: '/outputs', Tty: false };
        createOptions.Labels = {};
        createOptions.Labels[WORKFLOW_KEY] = workflowId;
        createOptions.Cmd = ['/bin/sh', '-c', `cp /inputs/${INPUT_FILE_NAME} /outputs/${OUTPUT_FILE_NAME}`];
        createOptions.HostConfig = {
          Binds: [
            `${getWorkflowOutputsPath(workflowId)}: /${OUTPUTS}: rw`,
            `${getWorkflowInputsPath(workflowId)}: /${INPUTS}: rw`,
          ],
        };

        docker.createContainer(createOptions, (createContainerError, container) => {
          console.log(`workflow=${workflowId} created container`);
          if (createContainerError !== null) {
            console.error({ log: 'error_creating_container', opts: createOptions, error: createContainerError });
            reject({ dockerCreateContainerOpts: createOptions, error: createContainerError });
            return;
          }

          container.start((containerStartError) => {
            console.log(`workflow=${workflowId} started container`);
            if (containerStartError !== null) {
              const result = { container, error: containerStartError };
              console.error(result);
              reject(result);
              return;
            }
            processContainerEnd(workflowId);
            // container.wait((waitError, endResult) => {
            //   const result = { container: container, error: waitError, result : endResult };
            //   resolve(result);
            // });
          });
        });
      });
    })
    .then(() => {
      console.log('Finished docker container ');
    }, (err) => {
      console.error(err);
      setWorkflowState(workflowId, WORKFLOW_STATE.failed);
      redis.hset(REDIS_WORKFLOW_ERRORS, workflowId, JSON.stringify(err));
      fs.deleteDirSync(getWorkflowPath(workflowId));
    });
}

/**
 * @returns {[Promise]}
 */
function getWorkflowState(workflowId) {
  return redis.hget(REDIS_WORKFLOW_STATUS, workflowId)
    .then((state) => {
      let normalizedState = state;
      if (state === null) {
        normalizedState = WORKFLOW_STATE.none;
      }
      return normalizedState;
    }).catch(console.error.bind(console));
}

// In case of crashes, check all running workflows and attach listeners to the
// running containers
function reattachExistingWorkflowContainers() {
  redis.hkeys(REDIS_WORKFLOW_STATUS)
    .then((keys) => {
      keys.forEach((workflowId) => {
        getWorkflowState(workflowId)
          .then((state) => {
            if (state === WORKFLOW_STATE.running) {
              console.log(`workflowId=${workflowId} running, reattaching listener to container`);
              processContainerEnd(workflowId);
            }
          }, (err) => {
            console.log(err);
          });
      });
    }, (err) => {
      console.error(err);
    });
}
reattachExistingWorkflowContainers();

router.get('/stdout/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  res.sendFile(getWorkflowStdoutPath(workflowId));
});

router.get('/stderr/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  res.sendFile(getWorkflowStderrPath(workflowId));
});

router.get('/exitcode/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  res.sendFile(getWorkflowExitCodePath(workflowId));
});

router.get('/temp/:workflowId', (req, res, next) => {
  const workflowId = req.params.workflowId;

  redis.hget(REDIS_WORKFLOWS, workflowId).then((workflowString) => {
    if (!workflowString) {
      const error = new Error(
        `No workflow found for given workflow id ${workflowId}`
      );
      error.status = 404;
      return next(error);
    }

    return res.send(JSON.parse(workflowString));
  }).catch(next);
});

router.get('/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  if (workflowId === null) {
    res.status(400).send({ error: 'No workflow id provided' });
  } else {
    try {
      fs.access(getWorkflowExitCodePath(workflowId));
      const outputPath = path.join(getWorkflowOutputsPath(workflowId), OUTPUT_FILE_NAME);
      res.sendFile(outputPath);
    } catch (err) {
      getWorkflowState(workflowId)
        .then((state) => {
          if (state === WORKFLOW_STATE.finished) {
            // Return the final data
            const outputPath = path.join(getWorkflowOutputsPath(workflowId), OUTPUT_FILE_NAME);
            res.sendFile(outputPath);
          } else if (state === WORKFLOW_STATE.failed) {
            redis.hget(REDIS_WORKFLOW_ERRORS, workflowId)
              .then((storedError) => {
                res.status(500).send({ state, error: storedError });
              }, (errHget) => {
                res.status(500).send({ error: errHget });
              });
          } else {
            res.status(400).send({ state });
          }
        }, (errGetState) => {
          res.status(500).send({ error: errGetState });
        });
    }
  }
});

/**
 * This route can be polled
 */
router.get('/state/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  if (workflowId === null) {
    res.status(400).send({ error: 'No workflow id provided' });
  } else {
    try {
      fs.access(getWorkflowExitCodePath(workflowId));
      res.send({ workflowId, state: WORKFLOW_STATE.finished });
    } catch (err) {
      console.error(err);
    }
    getWorkflowState(workflowId)
      .then((state) => {
        getDockerContainer(workflowId)
          .then((container) => {
            res.send({ workflowId, state, container });
          }, (err) => {
            res.send({ workflowId, state, error: err });
          });
      }, (err) => {
        res.status(500).send({ error: err });
      });
  }
});

router.post('/run', (req, res, next) => {
  if (!req.body.workflowId) {
    return next(new Error('Missing required parameter "workflowId"'));
  }
  if (!req.body.email) {
    return next(new Error('Missing required parameter "email"'));
  }
  if (!isEmail(req.body.email)) {
    return next(new Error('Invalid email given'));
  }
  if (!req.body.pdbUrl) {
    return next(new Error('Missing required parameter "pdbUrl"'));
  }

  const workflowId = req.body.workflowId.toString();
  const runId = shortId.generate();

  // Add the email to the set of emails to notify
  // when this workflow is complete
  // TODO we can probably use the email in the run instead
  const emailPromise = redis.sadd(REDIS_WORKFLOW_EMAIL_SET, req.body.email);

  const runPromise = redis.hset(REDIS_RUNS, runId, JSON.stringify({
    workflowId,
    email: req.body.email,
    pdbUrl: req.body.pdbUrl,
  }));

  const statePromise = setWorkflowState(
    workflowId, WORKFLOW_STATE.running
  );

  return Promise.all([emailPromise, runPromise, statePromise]).then(() => {
    executeWorkflow(workflowId);
    res.send({ runId });
  }).catch(next);
});

module.exports = router;
<commit_msg>Workflow can run<commit_after>/**
 * workflow routes
 */
const Docker = require('dockerode');
const fs = require('fs-extended');
const path = require('path');
const Promise = require('bluebird');
const express = require('express');
const hash = require('object-hash');
const promiseRedis = require('promise-redis');
const isEmail = require('validator').isEmail;
const shortId = require('shortid');

const router = new express.Router();

const WORKFLOW_TEMP_FOLDER = '/tmp/workflow_downloads/';
const WORKFLOW_WORK_FOLDER = '/tmp/workflows/';
const INPUTS = 'inputs';
const OUTPUTS = 'outputs';

/* Redis constants */
const REDIS_WORKFLOWS = 'workflows';
const REDIS_RUNS = 'runs';
const REDIS_WORKFLOW_EMAIL_SET = 'workflow_emails';// redis<SET>
const REDIS_WORKFLOW_STATUS = 'workflow_status';// redis<HASH>
const REDIS_WORKFLOW_ERRORS = 'workflow_errors';// redis<HASH>

/* Docker image vars*/
const INPUT_FILE_NAME = 'input.json';
const OUTPUT_FILE_NAME = 'output.json';
const WORKFLOW_DOCKER_IMAGE = 'docker.io/busybox:latest';
const WORKFLOW_KEY = 'workflowId';

const WORKFLOW_STATE = {
  running: 'running',
  finished: 'finished',
  failed: 'failed',
  none: 'none',
};

const docker = new Docker({ socketPath: '/var/run/docker.sock' });

fs.ensureDirSync(WORKFLOW_TEMP_FOLDER);
fs.ensureDirSync(WORKFLOW_WORK_FOLDER);

const Redis = promiseRedis(resolver =>
  new Promise(resolver)
);
const redis = Redis.createClient({ host: 'localhost', port: 6379 });

/**
 * Hashes all files in a directory. If the files
 * are JSON, these will be hashed in a way where
 * semantically equivalent JSON objects will give
 * the same hash value.
 * This can be made async if needed.
 * @param  {[type]} path [description]
 * @return {[String]}      [description]
 */
function getWorkflowInputMd5(pathString) {
  const files = fs.listFilesSync(pathString, { recursive: true });
  files.sort();
  const hashes = [];
  files.forEach((e) => {
    const filePath = path.join(pathString, e);
    if (e.endsWith('.json')) {
      const data = fs.readFileSync(filePath, { encoding: 'utf8' });
      try {
        const jsobObj = JSON.parse(data);
        hashes.push(hash(jsobObj));
      } catch (err) {
        console.error(err);
        hashes.push(hash(data));
      }
    } else {
      const data = fs.readFileSync(filePath);
      hashes.push(hash(data));
    }
  });
  return hash(hashes);
}

/**
 * @return {[Promise]}
 */
function getDockerImage(dockerImage) {
  return new Promise((resolve, reject) => {
    docker.pull(dockerImage, (err, stream) => {
      if (err) {
        reject(err);
      } else {
        docker.modem.followProgress(stream, (finishedErr, output) => {
          // output is an array with output json parsed objects
          if (finishedErr) {
            reject(finishedErr);
          } else {
            console.log(output);
            resolve(true);
          }
        }, () => {});
      }
    });
  });
}

function getWorkflowPath(workflowId) {
  return path.join(WORKFLOW_WORK_FOLDER, workflowId);
}

function getWorkflowStdoutPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), 'stdout');
}

function getWorkflowStderrPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), 'stderr');
}

function getWorkflowExitCodePath(workflowId) {
  return path.join(getWorkflowPath(workflowId), 'exitCode');
}

function getWorkflowInputsPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), INPUTS);
}

function getWorkflowOutputsPath(workflowId) {
  return path.join(getWorkflowPath(workflowId), OUTPUTS);
}

/**
 * @returns {[Promise]}
 */
function getDockerContainer(workflowId) {
  const labelKey = `${WORKFLOW_KEY}=${workflowId}`;
  const labelFilter = JSON.stringify({ label: [labelKey] });

  return new Promise((resolve, reject) => {
    docker.listContainers({ all: true, filters: labelFilter }, (err, containers) => {
      if (err) {
        reject(err);
      } else if (containers.length > 0) {
        resolve(containers[0]);
      } else {
        resolve(null);
      }
    });
  });
}

/**
 * @returns {[Promise]}
 */
function writeContainerLogs(workflowId, container, isStdOut) {
  const pathString = isStdOut ?
    getWorkflowStdoutPath(workflowId) : getWorkflowStderrPath(workflowId);
  const opts = {
    stdout: isStdOut ? 0 : 1,
    stderr: !isStdOut ? 0 : 1,
  };
  return new Promise((resolve, reject) => {
    container.logs(opts, (err, logstream) => {
      if (err) {
        reject(err);
      } else if (logstream !== null) {
        let logs = '';
        logstream.on('end', () => {
          fs.writeFileSync(pathString, logs);
          resolve();
        });
        logstream.on('data', (data) => {
          logs += logs + data;
        });
      } else {
        fs.writeFileSync(pathString, '');
        resolve();
      }
    });
  });
}

function setWorkflowState(workflowId, state) {
  return new Promise((resolve, reject) => {
    if (!WORKFLOW_STATE[state]) {
      reject(`Unknown workflow state=${state}`);
    } else {
      resolve(redis.hset(REDIS_WORKFLOW_STATUS, workflowId, state));
    }
  });
}

function sendEmailsWorkflowEnded() {
}

function processContainerEnd(workflowId) {
  const exitcodePath = getWorkflowExitCodePath(workflowId);
  getDockerContainer(workflowId)
    .then((containerData) => {
      const container = docker.getContainer(containerData.Id);
      container.wait((err, result) => {
        console.log(`workflow=${workflowId} in container=${containerData.Id} finished with exitCode=${result.StatusCode}`);
        fs.writeFileSync(exitcodePath, result.StatusCode);

        writeContainerLogs(workflowId, container, true)
          .then(() =>
            writeContainerLogs(workflowId, container, false)
          )
          .then(() => {
            // Finally set the state
            setWorkflowState(
              workflowId,
              result.StatusCode === 0 ?
                WORKFLOW_STATE.finished : WORKFLOW_STATE.failed
            );
            // Then remove the container
            container.remove({ force: 1 }, (errRemove) => {
              if (errRemove) {
                console.error(`Workflow=${workflowId} Error removing container=${containerData.Id} err]${errRemove}`);
              }
            });
            sendEmailsWorkflowEnded(workflowId);
          }).catch(console.error.bind(console));
      });
    }).catch(console.error.bind(console));
}

/**
 * [executeWorkflow description]
 * @param  {[type]} workflowDir [description]
 * @return {[Promise<Bool>]}             [description]
 */
function executeWorkflow(workflowId) {
  console.log(`executeWorkflow ${workflowId}`);
  getDockerImage(WORKFLOW_DOCKER_IMAGE)
    .then(() => {
      console.log('got image');
      return new Promise((resolve, reject) => {
        console.log(`executeWorkflow workflowId=${workflowId}`);

        const createOptions = { Image: WORKFLOW_DOCKER_IMAGE, WorkingDir: '/outputs', Tty: false };
        createOptions.Labels = {};
        createOptions.Labels[WORKFLOW_KEY] = workflowId;
        createOptions.Cmd = ['/bin/sh', '-c', `cp /inputs/${INPUT_FILE_NAME} /outputs/${OUTPUT_FILE_NAME}`];
        createOptions.HostConfig = {
          Binds: [
            `${getWorkflowOutputsPath(workflowId)}:/${OUTPUTS}:rw`,
            `${getWorkflowInputsPath(workflowId)}:/${INPUTS}:rw`,
          ],
        };

        docker.createContainer(createOptions, (createContainerError, container) => {
          console.log(`workflow=${workflowId} created container`);
          if (createContainerError !== null) {
            console.error({ log: 'error_creating_container', opts: createOptions, error: createContainerError });
            reject({ dockerCreateContainerOpts: createOptions, error: createContainerError });
            return;
          }

          container.start((containerStartError) => {
            console.log(`workflow=${workflowId} started container`);
            if (containerStartError !== null) {
              const result = { container, error: containerStartError };
              console.error(result);
              reject(result);
              return;
            }
            processContainerEnd(workflowId);
            // container.wait((waitError, endResult) => {
            //   const result = { container: container, error: waitError, result: endResult };
            //   resolve(result);
            // });
          });
        });
      });
    })
    .then(() => {
      console.log('Finished docker container ');
    }, (err) => {
      console.error(err);
      setWorkflowState(workflowId, WORKFLOW_STATE.failed);
      redis.hset(REDIS_WORKFLOW_ERRORS, workflowId, JSON.stringify(err));
      fs.deleteDirSync(getWorkflowPath(workflowId));
    });
}

/**
 * @returns {[Promise]}
 */
function getWorkflowState(workflowId) {
  return redis.hget(REDIS_WORKFLOW_STATUS, workflowId)
    .then((state) => {
      let normalizedState = state;
      if (state === null) {
        normalizedState = WORKFLOW_STATE.none;
      }
      return normalizedState;
    }).catch(console.error.bind(console));
}

// In case of crashes, check all running workflows and attach listeners to the
// running containers
function reattachExistingWorkflowContainers() {
  redis.hkeys(REDIS_WORKFLOW_STATUS)
    .then((keys) => {
      keys.forEach((workflowId) => {
        getWorkflowState(workflowId)
          .then((state) => {
            if (state === WORKFLOW_STATE.running) {
              console.log(`workflowId=${workflowId} running, reattaching listener to container`);
              processContainerEnd(workflowId);
            }
          }, (err) => {
            console.log(err);
          });
      });
    }, (err) => {
      console.error(err);
    });
}
reattachExistingWorkflowContainers();

router.get('/stdout/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  res.sendFile(getWorkflowStdoutPath(workflowId));
});

router.get('/stderr/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  res.sendFile(getWorkflowStderrPath(workflowId));
});

router.get('/exitcode/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  res.sendFile(getWorkflowExitCodePath(workflowId));
});

router.get('/temp/:workflowId', (req, res, next) => {
  const workflowId = req.params.workflowId;

  redis.hget(REDIS_WORKFLOWS, workflowId).then((workflowString) => {
    if (!workflowString) {
      const error = new Error(
        `No workflow found for given workflow id ${workflowId}`
      );
      error.status = 404;
      return next(error);
    }

    return res.send(JSON.parse(workflowString));
  }).catch(next);
});

router.get('/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  if (workflowId === null) {
    res.status(400).send({ error: 'No workflow id provided' });
  } else {
    try {
      fs.access(getWorkflowExitCodePath(workflowId));
      const outputPath = path.join(getWorkflowOutputsPath(workflowId), OUTPUT_FILE_NAME);
      res.sendFile(outputPath);
    } catch (err) {
      getWorkflowState(workflowId)
        .then((state) => {
          if (state === WORKFLOW_STATE.finished) {
            // Return the final data
            const outputPath = path.join(getWorkflowOutputsPath(workflowId), OUTPUT_FILE_NAME);
            res.sendFile(outputPath);
          } else if (state === WORKFLOW_STATE.failed) {
            redis.hget(REDIS_WORKFLOW_ERRORS, workflowId)
              .then((storedError) => {
                res.status(500).send({ state, error: storedError });
              }, (errHget) => {
                res.status(500).send({ error: errHget });
              });
          } else {
            res.status(400).send({ state });
          }
        }, (errGetState) => {
          res.status(500).send({ error: errGetState });
        });
    }
  }
});

/**
 * This route can be polled
 */
router.get('/state/:workflowId', (req, res) => {
  const workflowId = req.params.workflowId;
  if (workflowId === null) {
    res.status(400).send({ error: 'No workflow id provided' });
  } else {
    try {
      fs.access(getWorkflowExitCodePath(workflowId));
      res.send({ workflowId, state: WORKFLOW_STATE.finished });
    } catch (err) {
      console.error(err);
    }
    getWorkflowState(workflowId)
      .then((state) => {
        getDockerContainer(workflowId)
          .then((container) => {
            res.send({ workflowId, state, container });
          }, (err) => {
            res.send({ workflowId, state, error: err });
          });
      }, (err) => {
        res.status(500).send({ error: err });
      });
  }
});

router.post('/run', (req, res, next) => {
  if (!req.body.workflowId) {
    return next(new Error('Missing required parameter "workflowId"'));
  }
  if (!req.body.email) {
    return next(new Error('Missing required parameter "email"'));
  }
  if (!isEmail(req.body.email)) {
    return next(new Error('Invalid email given'));
  }
  if (!req.body.pdbUrl) {
    return next(new Error('Missing required parameter "pdbUrl"'));
  }

  const workflowId = req.body.workflowId.toString();
  const runId = shortId.generate();

  // Add the email to the set of emails to notify
  // when this workflow is complete
  // TODO we can probably use the email in the run instead
  const emailPromise = redis.sadd(REDIS_WORKFLOW_EMAIL_SET, req.body.email);

  const runPromise = redis.hset(REDIS_RUNS, runId, JSON.stringify({
    workflowId,
    email: req.body.email,
    pdbUrl: req.body.pdbUrl,
  }));

  const statePromise = setWorkflowState(
    workflowId, WORKFLOW_STATE.running
  );

  return Promise.all([emailPromise, runPromise, statePromise]).then(() => {
    executeWorkflow(workflowId);
    res.send({ runId });
  }).catch(next);
});

module.exports = router;
<|endoftext|>


<commit_before>/**
 * A simple JSON search
 * Requires jQuery (v 1.7+)
 *
 * @author  Mat Hayward - Erskine Design
 * @version  0.1
 */

 /* ==========================================================================
    Initialisation
    ========================================================================== */

var q, jsonFeedUrl = "/search.json",
    $searchForm = $("[data-search-form]"),
    $searchInput = $("[data-search-input]"),
    $resultTemplate = $("#search-result"),
    $resultsPlaceholder = $("[data-search-results]"),
    $foundContainer = $("[data-search-found]"),
    $foundTerm = $("[data-search-found-term]"),
    $foundCount = $("[data-search-found-count]"),
    allowEmpty = true,
    showLoader = true,
    loadingClass = "is--loading";

$(document).ready( function() {

    // hide items found string
    $foundContainer.hide();

    // initiate search functionality
    initSearch();
});

 /* ==========================================================================
    Search functions
    ========================================================================== */

/**
 * Initiate search functionality.
 * Shows results based on querystring if present.
 * Binds search function to form submission.
 */
function initSearch() {

    // Get search results if q parameter is set in querystring
    if (getParameterByName('q')) {
        q = decodeURIComponent(getParameterByName('q'));
        $searchInput.val(q);
        execSearch(q);
    }

    // Get search results on submission of form
    $(document).on("submit", $searchForm, function(e) {
        e.preventDefault();
        q = $searchInput.val();
        execSearch(q);
    });
}

/**
 * Executes search
 * @param {String} q
 * @return null
 */
function execSearch(q) {
    if (q != '' || allowEmpty) {
        if (showLoader) {
            toggleLoadingClass();
        }

        getSearchResults(processData());
    }
}

/**
 * Toggles loading class on results and found string
 * @return null
 */
function toggleLoadingClass() {
    $resultsPlaceholder.toggleClass(loadingClass);
    $foundContainer.toggleClass(loadingClass);
}

/**
 * Get Search results from JSON
 * @param {Function} callbackFunction
 * @return null
 */
function getSearchResults(callbackFunction) {
    $.get(BASE_URL + jsonFeedUrl, callbackFunction, 'json');
}

/**
 * Process search result data
 * @return null
 */
function processData() {
    $results = [];

    return function(data) {

        var resultsCount = 0,
            results = "";

        $.each(data, function(index, item) {
            // check if search term is in content or title
            if (item.excerpt.toLowerCase().indexOf(q.toLowerCase()) > -1 || item.title.toLowerCase().indexOf(q.toLowerCase()) > -1) {
                var result = populateResultContent($resultTemplate.html(), item);
                resultsCount++;
                results += result;
            }
        });

        if (showLoader) {
            toggleLoadingClass();
        }

        populateResultsString(resultsCount);
        showSearchResults(results);
    }
}

/**
 * Add search results to placeholder
 * @param {String} results
 * @return null
 */
function showSearchResults(results) {
    // Add results HTML to placeholder
    $resultsPlaceholder.html(results);
}

/**
 * Add results content to item template
 * @param {String} html
 * @param {object} item
 * @return {String} Populated HTML
 */
function populateResultContent(html, item) {
    html = injectContent(html, item.title, '##Title##');
    html = injectContent(html, item.link, '##Url##');
    html = injectContent(html, item.excerpt, '##Excerpt##');
    html = injectContent(html, item.date, '##Date##');
    return html;
}

/**
 * Populates results string
 * @param {String} count
 * @return null
 */
function populateResultsString(count) {
    $foundTerm.text(q);
    $foundCount.text(count);
    $foundContainer.show();
}

 /* ==========================================================================
    Helper functions
    ========================================================================== */

/**
 * Gets query string parameter - taken from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
 * @param {String} name
 * @return {String} parameter value
 */
function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

/**
 * Injects content into template using placeholder
 * @param {String} originalContent
 * @param {String} injection
 * @param {String} placeholder
 * @return {String} injected content
 */
function injectContent(originalContent, injection, placeholder) {
    var regex = new RegExp(placeholder, 'g');
    return originalContent.replace(regex, injection);
}<commit_msg>Refer search form with the class to target the intended form only - to fix #155<commit_after>/**
 * A simple JSON search
 * Requires jQuery (v 1.7+)
 *
 * @author  Mat Hayward - Erskine Design
 * @version  0.1
 */

 /* ==========================================================================
    Initialisation
    ========================================================================== */

var q, jsonFeedUrl = "/search.json",
    $searchForm = $(".simple-search"),
    $searchInput = $("[data-search-input]"),
    $resultTemplate = $("#search-result"),
    $resultsPlaceholder = $("[data-search-results]"),
    $foundContainer = $("[data-search-found]"),
    $foundTerm = $("[data-search-found-term]"),
    $foundCount = $("[data-search-found-count]"),
    allowEmpty = true,
    showLoader = true,
    loadingClass = "is--loading";

$(document).ready( function() {

    // hide items found string
    $foundContainer.hide();

    // initiate search functionality
    initSearch();
});

 /* ==========================================================================
    Search functions
    ========================================================================== */

/**
 * Initiate search functionality.
 * Shows results based on querystring if present.
 * Binds search function to form submission.
 */
function initSearch() {

    // Get search results if q parameter is set in querystring
    if (getParameterByName('q')) {
        q = decodeURIComponent(getParameterByName('q'));
        $searchInput.val(q);
        execSearch(q);
    }

    // Get search results on submission of form
    $(document).on("submit", $searchForm, function(e) {
        e.preventDefault();
        q = $searchInput.val();
        execSearch(q);
    });
}

/**
 * Executes search
 * @param {String} q
 * @return null
 */
function execSearch(q) {
    if (q != '' || allowEmpty) {
        if (showLoader) {
            toggleLoadingClass();
        }

        getSearchResults(processData());
    }
}

/**
 * Toggles loading class on results and found string
 * @return null
 */
function toggleLoadingClass() {
    $resultsPlaceholder.toggleClass(loadingClass);
    $foundContainer.toggleClass(loadingClass);
}

/**
 * Get Search results from JSON
 * @param {Function} callbackFunction
 * @return null
 */
function getSearchResults(callbackFunction) {
    $.get(BASE_URL + jsonFeedUrl, callbackFunction, 'json');
}

/**
 * Process search result data
 * @return null
 */
function processData() {
    $results = [];

    return function(data) {

        var resultsCount = 0,
            results = "";

        $.each(data, function(index, item) {
            // check if search term is in content or title
            if (item.excerpt.toLowerCase().indexOf(q.toLowerCase()) > -1 || item.title.toLowerCase().indexOf(q.toLowerCase()) > -1) {
                var result = populateResultContent($resultTemplate.html(), item);
                resultsCount++;
                results += result;
            }
        });

        if (showLoader) {
            toggleLoadingClass();
        }

        populateResultsString(resultsCount);
        showSearchResults(results);
    }
}

/**
 * Add search results to placeholder
 * @param {String} results
 * @return null
 */
function showSearchResults(results) {
    // Add results HTML to placeholder
    $resultsPlaceholder.html(results);
}

/**
 * Add results content to item template
 * @param {String} html
 * @param {object} item
 * @return {String} Populated HTML
 */
function populateResultContent(html, item) {
    html = injectContent(html, item.title, '##Title##');
    html = injectContent(html, item.link, '##Url##');
    html = injectContent(html, item.excerpt, '##Excerpt##');
    html = injectContent(html, item.date, '##Date##');
    return html;
}

/**
 * Populates results string
 * @param {String} count
 * @return null
 */
function populateResultsString(count) {
    $foundTerm.text(q);
    $foundCount.text(count);
    $foundContainer.show();
}

 /* ==========================================================================
    Helper functions
    ========================================================================== */

/**
 * Gets query string parameter - taken from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
 * @param {String} name
 * @return {String} parameter value
 */
function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

/**
 * Injects content into template using placeholder
 * @param {String} originalContent
 * @param {String} injection
 * @param {String} placeholder
 * @return {String} injected content
 */
function injectContent(originalContent, injection, placeholder) {
    var regex = new RegExp(placeholder, 'g');
    return originalContent.replace(regex, injection);
}<|endoftext|>


<commit_before>/*
* Dialogs; v20140104
* https://github.com/yceruto/bootstrap-dialogs
* Copyright (c) 2014 Yonel Ceruto Glez
*/

if (typeof jQuery === "undefined") { throw new Error("Dialogs requires jQuery") }

const DIALOG_INFORMATION = DIALOG_PRIMARY = 0,
      DIALOG_QUESTION = DIALOG_INFO = 1,
      DIALOG_SUCCESS = 2,
      DIALOG_WARNING = 3,
      DIALOG_EXCLAMATION = DIALOG_DANGER= 4,
      DIALOG_INVERT = 5;

/**
 * @example
 * DialogBox('Buy?', {
 *    type: DIALOG_QUESTION,                         //Default DIALOG_INFORMATION
 *    title: 'Question',                             //Default 'Information'
 *    buttons: {'Yes': function(){...}, 'No': null}, //Default OK button
 *    onClose: function(){...}                       //Default null
 * })
 *
 */
var DialogBox = function (content, options) {
    if (!$.isPlainObject(options))
        options = {};

    options = $.extend({}, DialogBox.DEFAULTS, options);

    var $modal = $(options.template),
        $footer = $modal.find('.modal-footer'),
        headerClass, buttonClass,

        createButton = function (content, onClick){
            var $button = $(options.buttonTemplate);
            $button.text(content);
            if (onClick && $.isFunction(onClick))
                $button.click(function(){
                    onClick(this, $modal)
                });
            else
                $button.attr('data-dismiss', 'modal');
            return $button;
        };

    if (!$.isPlainObject(options.buttons) || $.isEmptyObject(options.buttons))
        $footer.append(createButton(options.buttonOK));
    else
        for (var index in options.buttons)
            $footer.append(createButton(index, options.buttons[index]));

    switch (options.type) {
        case DIALOG_QUESTION:
            headerClass = 'modal-header-info';
            buttonClass = 'btn-info';
            break;
        case DIALOG_SUCCESS:
            headerClass = 'modal-header-success';
            buttonClass = 'btn-success';
            break;
        case DIALOG_WARNING:
            headerClass = 'modal-header-warning';
            buttonClass = 'btn-warning';
            break;
        case DIALOG_EXCLAMATION:
            headerClass = 'modal-header-danger';
            buttonClass = 'btn-danger';
            break;
        case DIALOG_INVERT:
            headerClass = 'modal-header-invert';
            buttonClass = 'btn-default';
            break;
        default:
            buttonClass = 'btn-primary';
            break;
    }

    if (headerClass)
        $modal.find('.modal-header').addClass(headerClass);

    $footer.find('button:first').removeClass('btn-default').addClass(buttonClass);
    $footer.attr('style', 'text-align: ' + options.buttonAlign + ';');

    $modal.find('.modal-dialog').innerWidth(options.width);
    $modal.find('.modal-title').html(options.title);
    $modal.find('.modal-body').html(content);

    $modal.on('hidden.bs.modal', function(){
        if (options.onClose)
            options.onClose();
        $modal.remove();
    });

    $('body').append($modal);

    $modal.modal('show');
};

DialogBox.DEFAULTS = {
    template: '

<button type="button" id="btnClose" class="close" data-dismiss="modal" aria-hidden="true">×</button>

',
    width: 500,
    title: 'Information',
    type: DIALOG_INFORMATION,
    buttonTemplate: '<button type="button" class="btn btn-default" data-dismiss="modal"></button>',
    buttonOK: 'OK',
    buttonAlign: 'center',
    onClose: null
};

/**
 * @example
 * MessageBox('Buy?', {
 *    type: DIALOG_QUESTION,                         //Default DIALOG_INFORMATION
 *    title: 'Question',                             //Default 'Information'
 *    icon: 'glyphicon-shopping-cart',               //Default 'glyphicon-info-sign'
 *    buttons: {'Yes': function(){...}, 'No': null}, //Default OK button
 *    onClose: function(){...}                       //Default null
 * })
 *
 */
var MessageBox = function (message, options) {
    options  = $.extend({}, MessageBox.DEFAULTS, options);

    var $content = $(options.iconTemplate),
        iconClass;

    if (options.icon && typeof options.icon !== 'string')
        options.icon = null;

    switch (options.type) {
        case DIALOG_QUESTION:
            iconClass = 'text-info ' + (options.icon || 'glyphicon-question-sign');
            break;
        case DIALOG_SUCCESS:
            iconClass = 'text-success ' + (options.icon || 'glyphicon-info-sign');
            break;
        case DIALOG_WARNING:
            iconClass = 'text-warning ' + (options.icon || 'glyphicon-warning-sign');
            break;
        case DIALOG_EXCLAMATION:
            iconClass = 'text-danger ' + (options.icon || 'glyphicon-exclamation-sign');
            break;
        case DIALOG_INVERT:
            iconClass = 'text-muted ' + (options.icon || 'glyphicon-info-sign');
            break;
        default:
            iconClass = 'text-primary ' + (options.icon || options.iconClass);
            break;
    }

    $content.find('#col-icon').html(' ');
    $content.find('#col-text').html(message);

    DialogBox($content, options);
};

MessageBox.DEFAULTS = {
    iconTemplate: '

',
    iconClass: 'glyphicon-info-sign',
    iconSize: 32
};

/**
 * @example
 * FormBox('/login', {
 *    type: DIALOG_INFO,                                  //Default DIALOG_PRIMARY
 *    title: 'Login',                                     //Default 'Form'
 *    buttons: {'Save': function(){...}, 'Cancel': null}, //Default 'Close' and 'Save changes' buttons with ajaxSubmit (jquery.form.min.js is required)
 *    onSaveCallback: function(){...}                     //Only when the 'buttons' option is empty
 * })
 *
 */
var FormBox = function (url, options) {
    options = $.extend({}, FormBox.DEFAULTS, options);

    if (!$.isPlainObject(options.buttons))
        options.buttons = {
            'Save changes': function(sender, $modal) {
                var $form = $modal.find('form'),
                    url = $form.attr('action'),
                    method = $form.attr('method'),
                    data = new FormData($form[0]);

                if ($.isFunction($form.ajaxSubmit)) {
                    $form.ajaxSubmit({
                        success: function (response) {
                            switch (response.status) {
                                case 200:
                                    if (options.onSaveCallback && $.isFunction(options.onSaveCallback))
                                        options.onSaveCallback($modal, response.content);
                                    else
                                        $modal.find('.modal-body').html(response.content);
                                    break;
                                case 301:
                                case 302:
                                    $modal.on('hidden.bs.modal', function(){
                                        window.location.href = response.content;
                                    });
                                    $modal.modal('hide');
                                    break;
                                default:
                                    MessageBox(response.content, {
                                        type: DIALOG_EXCLAMATION,
                                        title: response.status + ' ' + response.statusText
                                    });
                                    break;
                            }
                        }
                    });
                } else {
                    MessageBox('FormBox requires jquery.form.min.js', {
                        type: DIALOG_EXCLAMATION,
                        title: 'ajaxSubmit not found!'
                    });
                }
            },
            'Close': null
        };

    $.ajax({
        url: url,
        success: function(response) {
            switch (response.status) {
                case 200:
                    if (response.content.indexOf('<form ') > -1)
                        DialogBox(response.content, options);
                    else
                        MessageBox('The form tag is missing into content response (GET ' + url + '). The FormBox requires a form tag.', {
                            type: DIALOG_EXCLAMATION,
                            title: 'Form Missing'
                        });
                    break;
                default:
                    MessageBox(response.content, {
                        type: DIALOG_EXCLAMATION,
                        title: response.status + ' ' + response.statusText
                    });
                    break;
            }
        }
    });
};

FormBox.DEFAULTS = {
    title: 'Form',
    width: 600,
    buttonTemplate: '<button type="button" class="btn btn-default"></button>',
    buttonAlign: 'right',
    onSaveCallback: null
};<commit_msg>Remove the jquery form dependency. Supplant to FormData object.<commit_after>/*
* Dialogs; v20140104
* https://github.com/yceruto/bootstrap-dialogs
* Copyright (c) 2014 Yonel Ceruto Glez
*/

if (typeof jQuery === "undefined") { throw new Error("Dialogs requires jQuery") }

const DIALOG_INFORMATION = DIALOG_PRIMARY = 0,
      DIALOG_QUESTION = DIALOG_INFO = 1,
      DIALOG_SUCCESS = 2,
      DIALOG_WARNING = 3,
      DIALOG_EXCLAMATION = DIALOG_DANGER= 4,
      DIALOG_INVERT = 5;

/**
 * @example
 * DialogBox('Buy?', {
 *    type: DIALOG_QUESTION,                         //Default DIALOG_INFORMATION
 *    title: 'Question',                             //Default 'Information'
 *    buttons: {'Yes': function(){...}, 'No': null}, //Default OK button
 *    onClose: function(){...}                       //Default null
 * })
 *
 */
var DialogBox = function (content, options) {
    if (!$.isPlainObject(options))
        options = {};

    options = $.extend({}, DialogBox.DEFAULTS, options);

    var $modal = $(options.template),
        $footer = $modal.find('.modal-footer'),
        headerClass, buttonClass,

        createButton = function (content, onClick){
            var $button = $(options.buttonTemplate);
            $button.text(content);
            if (onClick && $.isFunction(onClick))
                $button.click(function(){
                    onClick(this, $modal)
                });
            else
                $button.attr('data-dismiss', 'modal');
            return $button;
        };

    if (!$.isPlainObject(options.buttons) || $.isEmptyObject(options.buttons))
        $footer.append(createButton(options.buttonOK));
    else
        for (var index in options.buttons)
            $footer.append(createButton(index, options.buttons[index]));

    switch (options.type) {
        case DIALOG_QUESTION:
            headerClass = 'modal-header-info';
            buttonClass = 'btn-info';
            break;
        case DIALOG_SUCCESS:
            headerClass = 'modal-header-success';
            buttonClass = 'btn-success';
            break;
        case DIALOG_WARNING:
            headerClass = 'modal-header-warning';
            buttonClass = 'btn-warning';
            break;
        case DIALOG_EXCLAMATION:
            headerClass = 'modal-header-danger';
            buttonClass = 'btn-danger';
            break;
        case DIALOG_INVERT:
            headerClass = 'modal-header-invert';
            buttonClass = 'btn-default';
            break;
        default:
            buttonClass = 'btn-primary';
            break;
    }

    if (headerClass)
        $modal.find('.modal-header').addClass(headerClass);

    $footer.find('button:first').removeClass('btn-default').addClass(buttonClass);
    $footer.attr('style', 'text-align: ' + options.buttonAlign + ';');

    $modal.find('.modal-dialog').innerWidth(options.width);
    $modal.find('.modal-title').html(options.title);
    $modal.find('.modal-body').html(content);

    $modal.on('hidden.bs.modal', function(){
        if (options.onClose)
            options.onClose();
        $modal.remove();
    });

    $('body').append($modal);

    $modal.modal('show');
};

DialogBox.DEFAULTS = {
    template: '

<button type="button" id="btnClose" class="close" data-dismiss="modal" aria-hidden="true">×</button>

',
    width: 500,
    title: 'Information',
    type: DIALOG_INFORMATION,
    buttonTemplate: '<button type="button" class="btn btn-default" data-dismiss="modal"></button>',
    buttonOK: 'OK',
    buttonAlign: 'center',
    onClose: null
};

/**
 * @example
 * MessageBox('Buy?', {
 *    type: DIALOG_QUESTION,                         //Default DIALOG_INFORMATION
 *    title: 'Question',                             //Default 'Information'
 *    icon: 'glyphicon-shopping-cart',               //Default 'glyphicon-info-sign'
 *    buttons: {'Yes': function(){...}, 'No': null}, //Default OK button
 *    onClose: function(){...}                       //Default null
 * })
 *
 */
var MessageBox = function (message, options) {
    options  = $.extend({}, MessageBox.DEFAULTS, options);

    var $content = $(options.iconTemplate),
        iconClass;

    if (options.icon && typeof options.icon !== 'string')
        options.icon = null;

    switch (options.type) {
        case DIALOG_QUESTION:
            iconClass = 'text-info ' + (options.icon || 'glyphicon-question-sign');
            break;
        case DIALOG_SUCCESS:
            iconClass = 'text-success ' + (options.icon || 'glyphicon-info-sign');
            break;
        case DIALOG_WARNING:
            iconClass = 'text-warning ' + (options.icon || 'glyphicon-warning-sign');
            break;
        case DIALOG_EXCLAMATION:
            iconClass = 'text-danger ' + (options.icon || 'glyphicon-exclamation-sign');
            break;
        case DIALOG_INVERT:
            iconClass = 'text-muted ' + (options.icon || 'glyphicon-info-sign');
            break;
        default:
            iconClass = 'text-primary ' + (options.icon || options.iconClass);
            break;
    }

    $content.find('#col-icon').html(' ');
    $content.find('#col-text').html(message);

    DialogBox($content, options);
};

MessageBox.DEFAULTS = {
    iconTemplate: '

',
    iconClass: 'glyphicon-info-sign',
    iconSize: 32
};

/**
 * @example
 * FormBox('/login', {
 *    type: DIALOG_INFO,                                  //Default DIALOG_PRIMARY
 *    title: 'Login',                                     //Default 'Form'
 *    buttons: {'Save': function(){...}, 'Cancel': null}, //Default 'Close' and 'Save changes' buttons
 *    onSaveCallback: function(){...}                     //Only when the 'buttons' option is empty
 * })
 *
 */
var FormBox = function (url, options) {

    options = $.extend({}, FormBox.DEFAULTS, options);

    if (!$.isPlainObject(options.buttons))
        options.buttons = {
            'Save changes': function(sender, $modal) {
                var $form = $modal.find('form'),
                    url = $form.attr('action'),
                    method = $form.attr('method'),
                    data = new FormData($form[0]);

                $.ajax({
                    url: url,
                    type: method,
                    data: data,
                    cache: false,
                    contentType: false,
                    processData: false,
                    success: function (response) {
                        switch (response.status) {
                            case 200:
                                if (options.onSaveCallback && $.isFunction(options.onSaveCallback))
                                    options.onSaveCallback($modal, response.content);
                                else if (isFormMissing(response.content))
                                    MessageBox(method.toUpperCase() + ' ' + url + '. The form tag is missing into content response. The FormBox requires a form.', {
                                        type: DIALOG_EXCLAMATION,
                                        title: 'Form Missing'
                                    });
                                else
                                    $modal.find('.modal-body').html(response.content);
                                break;
                            case 301:
                            case 302:
                                $modal.on('hidden.bs.modal', function(){
                                    window.location.href = response.content;
                                });
                                $modal.modal('hide');
                                break;
                            default:
                                MessageBox(response.content, {
                                    type: DIALOG_EXCLAMATION,
                                    title: response.status + ' ' + response.statusText
                                });
                                break;
                        }
                    }
                })
            },
            'Close': null
        };

    $.ajax({
        url: url,
        success: function(response) {
            switch (response.status) {
                case 200:
                    if (isFormMissing(response.content))
                        MessageBox('The form tag is missing into content response (GET ' + url + '). The FormBox requires a form tag.', {
                            type: DIALOG_EXCLAMATION,
                            title: 'Form Missing'
                        });
                    else
                        DialogBox(response.content, options);
                    break;
                default:
                    MessageBox(response.content, {
                        type: DIALOG_EXCLAMATION,
                        title: response.status + ' ' + response.statusText
                    });
                    break;
            }
        }
    });

    function isFormMissing(content) {
        return content.indexOf('<form ') == -1
    }
};

FormBox.DEFAULTS = {
    title: 'Form',
    width: 600,
    buttonTemplate: '<button type="button" class="btn btn-default"></button>',
    buttonAlign: 'right',
    onSaveCallback: null
};<|endoftext|>


<commit_before>var util = require('util'),
  os = require('os'),
  PythonShell = require('python-shell'),
  exec = require('child_process').exec,
  bleno = require('bleno'),
  gpio = require("pi-gpio"),
  Descriptor = bleno.Descriptor,
  Characteristic = bleno.Characteristic;

util.inherits(BatteryLevelCharacteristic, Characteristic);

BatteryLevelCharacteristic.prototype.onReadRequest = function(offset, callback) {

  console.log("in read");
  if (os.platform() === 'darwin') {
    exec('pmset -g batt', function (error, stdout, stderr) {

      var data = stdout.toString();
      // data - 'Now drawing from \'Battery Power\'\n -InternalBattery-0\t95%; discharging; 4:11 remaining\n'
      var percent = data.split('\t')[1].split(';')[0];
      console.log(percent);
      percent = parseInt(percent, 10);
      callback(this.RESULT_SUCCESS, new Buffer([percent]));

    });
  } else {
    // return hardcoded value
    console.log("reading...")
    callback(this.RESULT_SUCCESS, new Buffer([98]));

  }
};

BatteryLevelCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) {
  //console.log("in write");
  if (offset) {
    //console.log("in write - offset");
    callback(this.RESULT_ATTR_NOT_LONG);
  }
  //else if (data.length !== 1) {
   // console.log("data length !== 1");
    //console.log(JSON.stringify(data));
    //callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH);
  //}
  else {
    // console.log(JSON.stringify(data));
    // console.log('processing write request');
    // var receivedData = data.readUInt8(0);
    // console.log(typeof data);
    // console.log(data.toString('ascii'));

    // console.log(receivedData);
    // console.log(typeof receivedData);
        var command = data.toString('ascii');
    //console.log('command');
    console.log(command);

    if(command === "0"){
      console.log("stop");
      // print "Now stop"
      // GPIO.output(Motor1E,GPIO.LOW)

          gpio.open(22, "output", function(err) {
          if(err){
            console.log(err);
          }       // Open pin 22 for output
          gpio.write(22, 0, function() {            // Set pin 22 low (0)
              console.log("set pin 22 low");
            //gpio.close(22);                        // Close pin 22
          });
        });

    }
    else if(command === "1"){
      console.log("command left");

            PythonShell.defaultOptions = {
              scriptPath: './gpiopython'
            };

            var pyshell = new PythonShell('pwm.py', {
                mode: 'text'
            });

            var output = '';
            pyshell.stdout.on('data', function (data) {
                output += ''+data;
                console.log(output);
            });

            pyshell.send('left').end(function (err) {
                if (err) return console.log(err);
                console.log(output);

            });

    }else if(command === "2"){
      console.log("command right");

            PythonShell.defaultOptions = {
              scriptPath: './gpiopython'
            };

            var pyshell = new PythonShell('pwm.py', {
                pythonOptions: '-u'
            });

            var output = '';
            pyshell.stdout.on('data', function (data) {
                output += ''+data;
                console.log(output);
            });

            pyshell.send('right').end(function (err) {
                if (err) return console.log(err);
                console.log(output);

            });
    }
    else if(command === "3"){
      console.log("forward");

          gpio.open(16, "output", function(err) {
            if(err){
              console.log(err);
            }       // Open pin 16 for output
            gpio.write(16, 0, function() {            // Set pin 16 high (1)
                console.log("set pin 16 low");
              //gpio.close(16);                        // Close pin 16
              //18
              gpio.open(18, "output", function(err) {
                if(err){
                  console.log(err);
                }       // Open pin 18 for output
                gpio.write(18, 1, function() {            // Set pin 18 low (0)
                    console.log("set pin 18 high");
                  //gpio.close(18);                        // Close pin 18
                    gpio.open(22, "output", function(err) {
                          if(err){
                            console.log(err);
                          }       // Open pin 22 for output
                          gpio.write(22, 1, function() {            // Set pin 22 high (1)
                              console.log("set pin 22 high");
                            //gpio.close(22);                        // Close pin 22
                          });
                        });
                });
              });
            });
          });

    }
    else if(command === "4"){
      console.log("back");
                gpio.open(16, "output", function(err) {
            if(err){
              console.log(err);
            }       // Open pin 16 for output
            gpio.write(16, 1, function() {            // Set pin 16 high (1)
                console.log("set pin 16 high");
              //gpio.close(16);                        // Close pin 16
              //18
              gpio.open(18, "output", function(err) {
                if(err){
                  console.log(err);
                }       // Open pin 18 for output
                gpio.write(18, 0, function() {            // Set pin 18 low (0)
                    console.log("set pin 18 low");
                  //gpio.close(18);                        // Close pin 18
                    gpio.open(22, "output", function(err) {
                          if(err){
                            console.log(err);
                          }       // Open pin 22 for output
                          gpio.write(22, 1, function() {            // Set pin 22 high (1)
                              console.log("set pin 22 high");
                            //gpio.close(22);                        // Close pin 22
                          });
                        });
                });
              });
            });
          });
    }

  }
  callback(this.RESULT_SUCCESS);
};

function setMotorDirection(direction) {
    console.log('Setting: ' + direction);

          var pin16 = 0;
          var pin18 = 0;
          var pin22 = 0;

          if(direction === 'forward'){
            pin16 = 1;
            pin18 = 0;
            pin22 = 1;
          }else if (direction == 'reverse'){
            pin16 = 1;
            pin18 = 0;
            pin22 = 1;
          }

          gpio.open(16, "output", function(err) {
            if(err){
              console.log(err);
            }       // Open pin 16 for output
            gpio.write(16, pin16, function() {            // Set pin 16 high (1)
                console.log("set pin 16 " + pin16);
              //gpio.close(16);                        // Close pin 16
              //18
              gpio.open(18, "output", function(err) {
                if(err){
                  console.log(err);
                }       // Open pin 18 for output
                gpio.write(18, pin18, function() {            // Set pin 18 low (0)
                    console.log("set pin 18 " + pin18);
                  //gpio.close(18);                        // Close pin 18
                    gpio.open(22, "output", function(err) {
                          if(err){
                            console.log(err);
                          }       // Open pin 22 for output
                          gpio.write(22, pin22, function() {            // Set pin 22 high (1)
                              console.log("set pin 22 " + pin22);
                            //gpio.close(22);                        // Close pin 22
                          });
                        });
                });
              });
            });
          });
}

BatteryLevelCharacteristic.prototype.onNotify = function() {
  console.log('NotifyOnlyCharacteristic on notify');
};

module.exports = BatteryLevelCharacteristic;
<commit_msg>replace<commit_after>var util = require('util'),
  os = require('os'),
  PythonShell = require('python-shell'),
  exec = require('child_process').exec,
  bleno = require('bleno'),
  gpio = require("pi-gpio"),
  Descriptor = bleno.Descriptor,
  Characteristic = bleno.Characteristic;

var BatteryLevelCharacteristic = function() {
  BatteryLevelCharacteristic.super_.call(this, {
      uuid: '2A19',
      properties: ['read', 'write', 'notify'],
      descriptors: [
        new Descriptor({
            uuid: '2901',
            value: 'Battery level between 0 and 100 percent'
        }),
        new Descriptor({
            uuid: '2904',
            value: new Buffer([0x04, 0x01, 0x27, 0xAD, 0x01, 0x00, 0x00 ]) // maybe 12 0xC unsigned 8 bit
        })
      ]
  });
};

util.inherits(BatteryLevelCharacteristic, Characteristic);

BatteryLevelCharacteristic.prototype.onReadRequest = function(offset, callback) {

  console.log("in read");
  if (os.platform() === 'darwin') {
    exec('pmset -g batt', function (error, stdout, stderr) {

      var data = stdout.toString();
      // data - 'Now drawing from \'Battery Power\'\n -InternalBattery-0\t95%; discharging; 4:11 remaining\n'
      var percent = data.split('\t')[1].split(';')[0];
      console.log(percent);
      percent = parseInt(percent, 10);
      callback(this.RESULT_SUCCESS, new Buffer([percent]));

    });
  } else {
    // return hardcoded value
    console.log("reading...")
    callback(this.RESULT_SUCCESS, new Buffer([98]));

  }
};

BatteryLevelCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) {
  //console.log("in write");
  if (offset) {
    //console.log("in write - offset");
    callback(this.RESULT_ATTR_NOT_LONG);
  }
  //else if (data.length !== 1) {
   // console.log("data length !== 1");
    //console.log(JSON.stringify(data));
    //callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH);
  //}
  else {
    // console.log(JSON.stringify(data));
    // console.log('processing write request');
    // var receivedData = data.readUInt8(0);
    // console.log(typeof data);
    // console.log(data.toString('ascii'));

    // console.log(receivedData);
    // console.log(typeof receivedData);
        var command = data.toString('ascii');
    //console.log('command');
    console.log(command);

    if(command === "0"){
      console.log("stop");
      // print "Now stop"
      // GPIO.output(Motor1E,GPIO.LOW)

          gpio.open(22, "output", function(err) {
          if(err){
            console.log(err);
          }       // Open pin 22 for output
          gpio.write(22, 0, function() {            // Set pin 22 low (0)
              console.log("set pin 22 low");
            //gpio.close(22);                        // Close pin 22
          });
        });

    }
    else if(command === "1"){
      console.log("command left");

            PythonShell.defaultOptions = {
              scriptPath: './gpiopython'
            };

            var pyshell = new PythonShell('pwm.py', {
                mode: 'text'
            });

            var output = '';
            pyshell.stdout.on('data', function (data) {
                output += ''+data;
                console.log(output);
            });

            pyshell.send('left').end(function (err) {
                if (err) return console.log(err);
                console.log(output);

            });

    }else if(command === "2"){
      console.log("command right");

            PythonShell.defaultOptions = {
              scriptPath: './gpiopython'
            };

            var pyshell = new PythonShell('pwm.py', {
                pythonOptions: '-u'
            });

            var output = '';
            pyshell.stdout.on('data', function (data) {
                output += ''+data;
                console.log(output);
            });

            pyshell.send('right').end(function (err) {
                if (err) return console.log(err);
                console.log(output);

            });
    }
    else if(command === "3"){
      console.log("forward");

          gpio.open(16, "output", function(err) {
            if(err){
              console.log(err);
            }       // Open pin 16 for output
            gpio.write(16, 0, function() {            // Set pin 16 high (1)
                console.log("set pin 16 low");
              //gpio.close(16);                        // Close pin 16
              //18
              gpio.open(18, "output", function(err) {
                if(err){
                  console.log(err);
                }       // Open pin 18 for output
                gpio.write(18, 1, function() {            // Set pin 18 low (0)
                    console.log("set pin 18 high");
                  //gpio.close(18);                        // Close pin 18
                    gpio.open(22, "output", function(err) {
                          if(err){
                            console.log(err);
                          }       // Open pin 22 for output
                          gpio.write(22, 1, function() {            // Set pin 22 high (1)
                              console.log("set pin 22 high");
                            //gpio.close(22);                        // Close pin 22
                          });
                        });
                });
              });
            });
          });

    }
    else if(command === "4"){
      console.log("back");
                gpio.open(16, "output", function(err) {
            if(err){
              console.log(err);
            }       // Open pin 16 for output
            gpio.write(16, 1, function() {            // Set pin 16 high (1)
                console.log("set pin 16 high");
              //gpio.close(16);                        // Close pin 16
              //18
              gpio.open(18, "output", function(err) {
                if(err){
                  console.log(err);
                }       // Open pin 18 for output
                gpio.write(18, 0, function() {            // Set pin 18 low (0)
                    console.log("set pin 18 low");
                  //gpio.close(18);                        // Close pin 18
                    gpio.open(22, "output", function(err) {
                          if(err){
                            console.log(err);
                          }       // Open pin 22 for output
                          gpio.write(22, 1, function() {            // Set pin 22 high (1)
                              console.log("set pin 22 high");
                            //gpio.close(22);                        // Close pin 22
                          });
                        });
                });
              });
            });
          });
    }

  }
  callback(this.RESULT_SUCCESS);
};

function setMotorDirection(direction) {
    console.log('Setting: ' + direction);

          var pin16 = 0;
          var pin18 = 0;
          var pin22 = 0;

          if(direction === 'forward'){
            pin16 = 1;
            pin18 = 0;
            pin22 = 1;
          }else if (direction == 'reverse'){
            pin16 = 1;
            pin18 = 0;
            pin22 = 1;
          }

          gpio.open(16, "output", function(err) {
            if(err){
              console.log(err);
            }       // Open pin 16 for output
            gpio.write(16, pin16, function() {            // Set pin 16 high (1)
                console.log("set pin 16 " + pin16);
              //gpio.close(16);                        // Close pin 16
              //18
              gpio.open(18, "output", function(err) {
                if(err){
                  console.log(err);
                }       // Open pin 18 for output
                gpio.write(18, pin18, function() {            // Set pin 18 low (0)
                    console.log("set pin 18 " + pin18);
                  //gpio.close(18);                        // Close pin 18
                    gpio.open(22, "output", function(err) {
                          if(err){
                            console.log(err);
                          }       // Open pin 22 for output
                          gpio.write(22, pin22, function() {            // Set pin 22 high (1)
                              console.log("set pin 22 " + pin22);
                            //gpio.close(22);                        // Close pin 22
                          });
                        });
                });
              });
            });
          });
}

BatteryLevelCharacteristic.prototype.onNotify = function() {
  console.log('NotifyOnlyCharacteristic on notify');
};

module.exports = BatteryLevelCharacteristic;
<|endoftext|>


<commit_before>function GenomicOverviewConfig(nMut, nCna) {
    this.nMut = nMut;
    this.nCna = nCna;
    this.width = 1200;
    this.mutHeight = 6;
    this.cnaHeight = 20;
    this.rowMargin = 5;
    this.ticHeight = 10;
    this.yCurr = this.rowMargin;
    this.cnTh = [0.2,1.5];
}
GenomicOverviewConfig.prototype = {
    getCnColor: function(cnValue) {
        if (cnValue>=this.cnTh[1])
            return "#f00";
        if (cnValue<=-this.cnTh[1])
            return "#00f";
        var c = Math.round(255*(this.cnTh[1]-Math.abs(cnValue))/(this.cnTh[1]-this.cnTh[0]));
        if (cnValue<0)
            return "rgb("+c+","+c+",255)";
        else
            return "rgb(255,"+c+","+c+")";
    },
    canvasWidth: function() {
        return this.width + 5;
    },
    canvasHeight: function() {
        return this.yCurr+this.ticHeight+this.nMut*this.mutHeight+this.nCna*this.cnaHeight+(this.nMut+this.nCna)*this.rowMargin;
    }
};

function createRaphaelCanvas(elementId, config) {
    return Raphael(elementId, config.canvasWidth(), config.canvasHeight());
}

function getChmEndsPerc(chms, total) {
    var ends = [0];
    for (var i=1; i<chms.length; i++) {
        ends.push(ends[i-1]+chms[i]/total);
    }
    return ends;
}

/**
 * storing chromesome length info
 */
function ChmInfo() {
    this.hg19 = [0,249250621,243199373,198022430,191154276,180915260,171115067,159138663,146364022,141213431,135534747,135006516,133851895,115169878,107349540,102531392,90354753,81195210,78077248,59128983,63025520,48129895,51304566,155270560];
    this.total = 3036303846;
    this.perc = getChmEndsPerc(this.hg19,this.total);
}
ChmInfo.prototype = {
    loc2perc : function(chm,loc) {
        return this.perc[chm-1] + loc/this.total;
    },
    loc2scale : function(chm,loc,width) {
        return this.loc2perc(chm,loc) * width;
    },
    middle : function(chm, width) {
        var loc = this.hg19[chm]/2;
        return this.loc2scale(chm,loc,width);
    },
    chmName : function(chm) {
        if (chm == 23) {
            return "X/Y";
        }
        return chm;
    }
};

function plotChromosomes(p,config,chmInfo) {
    var yRuler = config.yCurr+config.ticHeight;
    drawLine(0,yRuler,config.width,yRuler,p);
    // ticks & texts
    for (var i=1; i<chmInfo.hg19.length; i++) {
        var xt = chmInfo.loc2scale(i,0,config.width);
        drawLine(xt,yRuler,xt,config.yCurr,p);

        var m = chmInfo.middle(i,config.width);
        p.text(m,yRuler-config.rowMargin,chmInfo.chmName(i));
    }
    drawLine(config.width,yRuler,config.width,config.yCurr,p);
    config.yCurr = yRuler+config.rowMargin;
}

function drawLine(x1, y1, x2, y2, p) {
    var path = "M" + x1 + " " + y1 + " L" + x2 + " " + y2;
    var line = p.path(path);
    line.attr("stroke", "#000");
    line.attr("stroke-width", 1);
    line.attr("opacity", 0.5);
    line.translate(0.5, 0.5);
}

function plotMuts(p,config,chmInfo,muts,chrCol,startCol,endCol) {
    for (var i=0; i<muts.length; i++) {
        var loc = extractLoc(muts[i],chrCol,[startCol,endCol]);
        if (loc==null) continue;
        var x = chmInfo.loc2scale(loc[0],(loc[1]+loc[2])/2,config.width);
        var y = config.yCurr + config.mutHeight/2;
        var c = p.circle(x,y,config.mutHeight/2);
        c.attr("fill","green");
        c.attr("stroke-width", 0)
    }
    config.yCurr += config.mutHeight + config.rowMargin;
}

function plotCnSegs(p,config,chmInfo,segs,chrCol,startCol,endCol,segCol) {
    for (var i=0; i<segs.length; i++) {
        var loc = extractLoc(segs[i],chrCol,[startCol,endCol,segCol]);
        if (loc==null) continue;
        var chm = loc[0];
        if (1 <= chm && chm <= 23) {
            var segMean = loc[3];
            if (Math.abs(segMean)>config.cnTh[0]&&chm<=chmInfo.hg19.length) {
                var start = loc[1];
                var end = loc[2];
                var x = chmInfo.loc2scale(chm,start,config.width);
                var w = chmInfo.loc2scale(1,end-start,config.width);
                var r = p.rect(x,config.yCurr,w,config.cnaHeight);
                var cl = config.getCnColor(segMean);
                r.attr("fill",cl);
                r.attr("stroke", cl);
                r.attr("stroke-width", 1);
                r.attr("opacity", 0.5);
                r.translate(0.5, 0.5);
            }
        }
    }
    config.yCurr += config.cnaHeight + config.rowMargin;
}

function extractLoc(data,chrCol,cols) {
    var chm = (data[chrCol]=='X'||data[chrCol]=='Y'||data[chrCol]=='x'||data[chrCol]=='y') ? 23 : parseInt(data[1]);
    if (isNaN(chm) || chm < 1 || chm > 23) {
        return null;
    }
    var ret = [chm];
    for (var i=0; i<cols.length; i++) {
        ret.push(data[cols[i]]);
    }
    return ret;
}<commit_msg>patient-view: filter cnv<commit_after>function GenomicOverviewConfig(nMut, nCna) {
    this.nMut = nMut;
    this.nCna = nCna;
    this.width = 1200;
    this.mutHeight = 6;
    this.cnaHeight = 20;
    this.rowMargin = 5;
    this.ticHeight = 10;
    this.yCurr = this.rowMargin;
    this.cnTh = [0.2,1.5];
    this.cnLengthTh = 50000;
}
GenomicOverviewConfig.prototype = {
    getCnColor: function(cnValue) {
        if (cnValue>=this.cnTh[1])
            return "#f00";
        if (cnValue<=-this.cnTh[1])
            return "#00f";
        var c = Math.round(255*(this.cnTh[1]-Math.abs(cnValue))/(this.cnTh[1]-this.cnTh[0]));
        if (cnValue<0)
            return "rgb("+c+","+c+",255)";
        else
            return "rgb(255,"+c+","+c+")";
    },
    canvasWidth: function() {
        return this.width + 5;
    },
    canvasHeight: function() {
        return this.yCurr+this.ticHeight+this.nMut*this.mutHeight+this.nCna*this.cnaHeight+(this.nMut+this.nCna)*this.rowMargin;
    }
};

function createRaphaelCanvas(elementId, config) {
    return Raphael(elementId, config.canvasWidth(), config.canvasHeight());
}

function getChmEndsPerc(chms, total) {
    var ends = [0];
    for (var i=1; i<chms.length; i++) {
        ends.push(ends[i-1]+chms[i]/total);
    }
    return ends;
}

/**
 * storing chromesome length info
 */
function ChmInfo() {
    this.hg19 = [0,249250621,243199373,198022430,191154276,180915260,171115067,159138663,146364022,141213431,135534747,135006516,133851895,115169878,107349540,102531392,90354753,81195210,78077248,59128983,63025520,48129895,51304566,155270560];
    this.total = 3036303846;
    this.perc = getChmEndsPerc(this.hg19,this.total);
}
ChmInfo.prototype = {
    loc2perc : function(chm,loc) {
        return this.perc[chm-1] + loc/this.total;
    },
    loc2scale : function(chm,loc,width) {
        return this.loc2perc(chm,loc) * width;
    },
    middle : function(chm, width) {
        var loc = this.hg19[chm]/2;
        return this.loc2scale(chm,loc,width);
    },
    chmName : function(chm) {
        if (chm == 23) {
            return "X/Y";
        }
        return chm;
    }
};

function plotChromosomes(p,config,chmInfo) {
    var yRuler = config.yCurr+config.ticHeight;
    drawLine(0,yRuler,config.width,yRuler,p);
    // ticks & texts
    for (var i=1; i<chmInfo.hg19.length; i++) {
        var xt = chmInfo.loc2scale(i,0,config.width);
        drawLine(xt,yRuler,xt,config.yCurr,p);

        var m = chmInfo.middle(i,config.width);
        p.text(m,yRuler-config.rowMargin,chmInfo.chmName(i));
    }
    drawLine(config.width,yRuler,config.width,config.yCurr,p);
    config.yCurr = yRuler+config.rowMargin;
}

function drawLine(x1, y1, x2, y2, p) {
    var path = "M" + x1 + " " + y1 + " L" + x2 + " " + y2;
    var line = p.path(path);
    line.attr("stroke", "#000");
    line.attr("stroke-width", 1);
    line.attr("opacity", 0.5);
    line.translate(0.5, 0.5);
}

function plotMuts(p,config,chmInfo,muts,chrCol,startCol,endCol) {
    for (var i=0; i<muts.length; i++) {
        var loc = extractLoc(muts[i],chrCol,[startCol,endCol]);
        if (loc==null) continue;
        var x = chmInfo.loc2scale(loc[0],(loc[1]+loc[2])/2,config.width);
        var y = config.yCurr + config.mutHeight/2;
        var c = p.circle(x,y,config.mutHeight/2);
        c.attr("fill","green");
        c.attr("stroke-width", 0)
    }
    config.yCurr += config.mutHeight + config.rowMargin;
}

function plotCnSegs(p,config,chmInfo,segs,chrCol,startCol,endCol,segCol) {
    for (var i=0; i<segs.length; i++) {
        var loc = extractLoc(segs[i],chrCol,[startCol,endCol,segCol]);
        if (loc==null) continue;
        var chm = loc[0];
        if (1 <= chm && chm <= 23) {
            var start = loc[1];
            var end = loc[2];
            var segMean = loc[3];
            if (Math.abs(segMean)<config.cnTh[0]||chm>chmInfo.hg19.length)
                continue;
            if (end-start<config.cnLengthTh) //filter cnv
                continue;
            var x = chmInfo.loc2scale(chm,start,config.width);
            var w = chmInfo.loc2scale(1,end-start,config.width);
            var r = p.rect(x,config.yCurr,w,config.cnaHeight);
            var cl = config.getCnColor(segMean);
            r.attr("fill",cl);
            r.attr("stroke", cl);
            r.attr("stroke-width", 1);
            r.attr("opacity", 0.5);
            r.translate(0.5, 0.5);
        }
    }
    config.yCurr += config.cnaHeight + config.rowMargin;
}

function extractLoc(data,chrCol,cols) {
    var chm = (data[chrCol]=='X'||data[chrCol]=='Y'||data[chrCol]=='x'||data[chrCol]=='y') ? 23 : parseInt(data[1]);
    if (isNaN(chm) || chm < 1 || chm > 23) {
        return null;
    }
    var ret = [chm];
    for (var i=0; i<cols.length; i++) {
        ret.push(data[cols[i]]);
    }
    return ret;
}<|endoftext|>


<commit_before>import ReactiveElement from "../core/ReactiveElement.js";
import {
  goFirst,
  goLast,
  goNext,
  goPrevious,
  itemsDelegate,
  setState,
} from "./internal.js";

/**
 * has the effect of adding the component to the tab order in document order.
 *
 * @module DelegateCursorMixin
 * @param {Constructor<ReactiveElement>} Base
 */
export default function DelegateCursorMixin(Base) {
  // The class prototype added by the mixin.
  class DelegateCursor extends Base {
    [goFirst]() {
      return delegateCursorOperation(this, goFirst);
    }

    [goLast]() {
      return delegateCursorOperation(this, goLast);
    }

    [goNext]() {
      return delegateCursorOperation(this, goNext);
    }

    [goPrevious]() {
      return delegateCursorOperation(this, goPrevious);
    }
  }

  return DelegateCursor;
}

function delegateCursorOperation(element, operation) {
  /** @type {any} */ const cast = element[itemsDelegate];
  if (!cast[operation]) {
    return false;
  }

  const changed = cast[operation]();
  if (changed) {
    const currentIndex = cast.currentIndex;
    element[setState]({ currentIndex });
  }

  return changed;
}
<commit_msg>Fix lint error.<commit_after>import ReactiveElement from "../core/ReactiveElement.js"; // eslint-disable-line no-unused-vars
import {
  goFirst,
  goLast,
  goNext,
  goPrevious,
  itemsDelegate,
  setState,
} from "./internal.js";

/**
 * has the effect of adding the component to the tab order in document order.
 *
 * @module DelegateCursorMixin
 * @param {Constructor<ReactiveElement>} Base
 */
export default function DelegateCursorMixin(Base) {
  // The class prototype added by the mixin.
  class DelegateCursor extends Base {
    [goFirst]() {
      return delegateCursorOperation(this, goFirst);
    }

    [goLast]() {
      return delegateCursorOperation(this, goLast);
    }

    [goNext]() {
      return delegateCursorOperation(this, goNext);
    }

    [goPrevious]() {
      return delegateCursorOperation(this, goPrevious);
    }
  }

  return DelegateCursor;
}

function delegateCursorOperation(element, operation) {
  /** @type {any} */ const cast = element[itemsDelegate];
  if (!cast[operation]) {
    return false;
  }

  const changed = cast[operation]();
  if (changed) {
    const currentIndex = cast.currentIndex;
    element[setState]({ currentIndex });
  }

  return changed;
}
<|endoftext|>