<commit_before>var _      = require('lodash'),
    log    = require('debug')('imdone-core:Project'),
    events = require('events'),
    fs     = require('fs'),
    async  = require('async'),
    util   = require('util'),
    path   = require('path'),
    wrench = require('wrench'),
    tools  = require('./tools'),
    List   = require('./list'),
    File   = require('./file'),
    Task   = require('./task');

var ERRORS         = { NOT_A_TASK: "task must be a Task",
                       LIST_NOT_FOUND: "List with name '%s' does not exist",
                       REPO_NOT_FOUND: "Repo %s not found" },
    CONFIG_DIR     = ".imdone",
    CONFIG_FILE    = "config.json",
    DEFAULT_CONFIG = { lists: [] };

function Project(owner, name, repos) {
  events.EventEmitter.call(this);
  this.configBase = process.env.IMDONE_CONFIG_DIR || tools.userHome();
  if (!fs.existsSync(this.configBase) || !fs.statSync(this.configBase).isDirectory())
    throw new Error("IMDONE_CONFIG_DIR must be an existing directory on the file system");

  this.owner = owner;
  this.name = name;
  this.repos = repos;
  this.busy = false;
  this.modifiedQ = [];
  this.config = this.loadConfig() || {};
  _.defaults(this.config, _.cloneDeep(DEFAULT_CONFIG));

  this.createListeners();
}

util.inherits(Project, events.EventEmitter);

// TODO:10 Need a way to run only one operation at a time on a project or repo. Use [kue](https://www.npmjs.org/package/kue)
// TODO:20 Decide which listeners we should expose at the project level
// emits project.modified, project.initialized, file.update, file.delete
Project.prototype.init = function(cb) {
  var self = this;
  cb = this.callIfBusySetIfNot(cb); 
  if (!cb) return;
  if (this.initialized) cb(); 
  var funcs = _.map(this.getRepos(), function(repo) {
    return function(cb) {
      self.initRepo(repo, cb);
    };
  });
  if (funcs.length > 0) {
    async.parallel(funcs, function(err, results) {
      self.storeLists(self.getProjectLists(), function() {
        cb();
      });
      self.emit('project.initialized', {project:self.getName()});
      // Start the modified interval
      self.modifiedInterval = setInterval(function() {
        if (self.modifiedQ.length > 0) {
          self.emit('project.modified', {project:self.name, mods:self.modifiedQ});
          self.modifiedQ = [];
        }
      }, 500);
      self.initialized = true;
    });
  } else {
    cb();
  }
};

Project.prototype.queueModified = function(data) {
  this.modifiedQ.push(data);
};

Project.prototype.callIfBusySetIfNot = function(cb) {
  cb = tools.cb(cb);
  if (this.isBusy()) {
    cb("Project is busy");
    return false;
  };

  this.busy = true;
  var self = this;
  
  return  function() {
    self.busy = false;
    cb.apply(self,arguments);
  };<commit_msg>Fail immediatly if no repos are found<commit_after>var _      = require('lodash'),
    log    = require('debug')('imdone-core:Project'),
    events = require('events'),
    fs     = require('fs'),
    async  = require('async'),
    util   = require('util'),
    path   = require('path'),
    wrench = require('wrench'),
    tools  = require('./tools'),
    List   = require('./list'),
    File   = require('./file'),
    Task   = require('./task');

var ERRORS         = { NOT_A_TASK: "task must be a Task",
                       LIST_NOT_FOUND: "List with name '%s' does not exist",
                       REPO_NOT_FOUND: "Repo %s not found" },
    CONFIG_DIR     = ".imdone",
    CONFIG_FILE    = "config.json",
    DEFAULT_CONFIG = { lists: [] };

function Project(owner, name, repos) {
  events.EventEmitter.call(this);
  this.configBase = process.env.IMDONE_CONFIG_DIR || tools.userHome();
  if (!fs.existsSync(this.configBase) || !fs.statSync(this.configBase).isDirectory())
    throw new Error("IMDONE_CONFIG_DIR must be an existing directory on the file system");

  this.owner = owner;
  this.name = name;
  this.repos = repos;
  this.busy = false;
  this.modifiedQ = [];
  this.config = this.loadConfig() || {};
  _.defaults(this.config, _.cloneDeep(DEFAULT_CONFIG));

  this.createListeners();
}

util.inherits(Project, events.EventEmitter);

// TODO:10 Need a way to run only one operation at a time on a project or repo. Use [kue](https://www.npmjs.org/package/kue)
// TODO:20 Decide which listeners we should expose at the project level
// emits project.modified, project.initialized, file.update, file.delete
Project.prototype.init = function(cb) {
  var self = this;
  cb = this.callIfBusySetIfNot(cb); 
  if (!cb) return;
  if (this.initialized) cb(); 
  var funcs = _.map(this.getRepos(), function(repo) {
    return function(cb) {
      self.initRepo(repo, cb);
    };
  });

  if (funcs.length === 0) return cb("Project has no Repos to initialize");

  async.parallel(funcs, function(err, results) {
    self.storeLists(self.getProjectLists(), function() {
      cb(null);
    });
    self.emit('project.initialized', {project:self.getName()});
    // Start the modified interval
    self.modifiedInterval = setInterval(function() {
      if (self.modifiedQ.length > 0) {
        self.emit('project.modified', {project:self.name, mods:self.modifiedQ});
        self.modifiedQ = [];
      }
    }, 500);
    self.initialized = true;
  });
};

Project.prototype.queueModified = function(data) {
  this.modifiedQ.push(data);
};

Project.prototype.callIfBusySetIfNot = function(cb) {
  cb = tools.cb(cb);
  if (this.isBusy()) {
    cb("Project is busy");
    return false;
  };

  this.busy = true;
  var self = this;
  
  return  function() {
    self.busy = false;
    cb.apply(self,arguments);
  };
{'fraction_non_alphanumeric': 0.15579322638146167, 'fraction_numerical': 0.0032085561497326204, 'mean_word_length': 3.0193409742120343, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 6, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3253072', 'n_tokens_mistral': 1826, 'n_tokens_neox': 1794, 'n_words': 409}