<commit_before>var Promise = require('es6-promise').Promise;
var cheerio = require("cheerio");
var fs      = require("fs");
var _       = require('underscore');
var moment  = require('moment');
var net     = require('net');
var path    = require('path');
var repl    = require('repl');
var csv     = require('csv');
var wkhtmltopdf   = require('wkhtmltopdf');
var config        = require('./lib/conjuror.config.js')
var argv          = require('./lib/conjuror.options.js');

// Run the imported options.
var args = argv.run();

// File Manipulation
var SaveFile = require('./lib/save_file');

// Conjuror
var Conjuror = require('./lib/conjuror.prepareRecipe.js');

// Load Conjuror Modules
Conjuror.Date = require('./lib/conjuror.date.js');
Conjuror.Trim = require('./lib/conjuror.trim.js');
Conjuror.Search = require('./lib/conjuror.search.js');


Conjuror.getClient = function(client_file, trim, callback) {
  // Takes as input a client.json file, the client slug to trim by,
  // the callback function which acts on the client object.
  Conjuror.readData(client_file, function(data) {
    // TODO: We need *way* better error handling here.
    if (data.data !== undefined && trim !== undefined) {

      data.data.splice(0,1);
      clientData = data.data;
      var foundClient = _.find(clientData, function(line, index){
        if (line) {

          var lineItem = Conjuror.murmurLineToSchema(line, data.schema);
          return lineItem.slug.toString() === trim.toString();
        } else {
          return false;
        }
      });

      return callback(Conjuror.murmurLineToSchema(foundClient, data.schema));
    } else {
      return callback(undefined);
    }
  });
}


Conjuror.summonUser = function(callback){
  // TODO: We should probably remove the depency on args here, and pass it in
  // as a variable.
  if (args.config !== undefined && args.config.user !== undefined){
    return callback(args.config.user);
  } else {
    return callback({'exists': false});
  }
};

Conjuror.magickData = function(data, schema, date) {

  var outputs = {
    totals: {
      hours: 0,
      money: 0.00,
    },
    csv: _.pluck(schema.fields, 'name').join(','),
    cli: '',
    html: ''
  };
  // Filter by Date / Type
  var date_filter = 'none';

  if (date !== undefined) {

    // TODO: would it be easier to just use moment.js for this?
    var is_date_full        = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
    var is_date_year_month  = /[0-9]{4}-[0-9]{2}/;
    var is_date_month_day   = /[0-9]{2}-[0-9]{2}/;
    var is_date_year        = /[0-9]{4}/;
    var is_week             = /week/;
    var is_month            = /month/;
    var is_today            = /today/;

    if (date.indexOf(':to:') > -1) {
      date_filter = 'range';
    }
    else if (is_week.exec(date)) {
      date_filter = 'this_week';
    } else if (is_today.exec(date)) {
      date_filter = 'today';
    } else if (is_month.exec(date)) {
      date_filter = 'this_month';
    } else if (is_date_full.exec(date)) {
      date_filter = 'full';
    } else if (is_date_year_month.exec(date)) {
      date_filter = 'year_month';
    } else if (is_date_month_day.exec(date)) {
      date_filter = 'month_day';
    } else if (is_date_year.exec(date)) {
      date_filter = 'year';
    } else {
      date_filter = 'month';
    }

    console.log('Filter by ' + date_filter + ': ' + date);
  }

  // Make Totals
  var increment_output = function(item) {

    outputs.totals.hours += item.time;
    outputs.totals.money += (item.time * item.rate);

    // CLI format
    outputs.cli += item.date + '\t ' + item.time + ' \t' + item.client + ' \t' + item.description + '\n';

    // HTML format
    if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {
      outputs.html += '<tr>\n';
      outputs.html += '   <td class="width-50">' + item.date + '</td>\n';
      outputs.html += '   <td class="width-50">' + item.client + '</td>\n';
      outputs.html += '   <td class="text-right">' + item.time + '</td>\n';
      outputs.html += '   <td class="text-left"> hrs</td>\n';
      outputs.html += '   <td>' + item.description + '</td>\n';
      outputs.html += '</tr>\n';
    }
  };

  _.each(data, function(line, index) {

    if (index !== 0){ // skip the first line
      var parts = line;

      // Filter Date & Trim
      var check_date    = Conjuror.Date[date_filter](parts[0], date);
      var check_trim    = Conjuror.Trim(args.options.trim, parts);
      var check_search  = Conjuror.Search(parts, args.options.search);

      // Does Item Meet Filter (date, trim)
      if (_.indexOf([check_date, check_trim, check_search], false) === -1) {

        // Build CSV format (FIXME: nasty code organization going on here)
        if (_.indexOf(args.options.format, 'csv') > -1) {
          outputs.csv += '\n' + line.join(',');
        }

        var item_output = Conjuror.murmurLineToSchema(line, schema);
        increment_output(item_output);
      }
    }
  });

  // Outputs
  outputs.totals.hours = outputs.totals.hours.toFixed(2);
  outputs.totals.money = outputs.totals.money.toFixed(2);
  return outputs;
}


Conjuror.castToCSV = function(outputs) {

  if (_.indexOf(args.options.format, 'csv') > -1) {

    // File Name
    var output_name = 'Conjuror Output - ' + moment().format('D MMMM YYYY');
    if (args.options.output) {
      output_name = args.options.output;
    }

    // Save CSV
    var saveFile = SaveFile(fs, 'output/' + output_name + '.csv', outputs.csv);
  }
}


// Output HTML
Conjuror.castToHTML = function(outputs, user){

  if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {

    // Get HTML Template
    var template = args.config.invoice_template || 'invoice';
    var template_path = './templates/' + template + '.html';

    Conjuror.readManuscript(template_path)

      .then(function(buffer) {
        console.log("Conjuror loaded template");
        var output_name = 'Invoice - ' + moment().format('D MMMM YYYY');

        if (outputs.client) {
          output_name = args.options.invoicenumber + ' - ' +
            outputs.client.name + ' - ' +
            moment().format('DD.MM.YYYY');
        } else if (args.options.output) {
          output_name = args.options.output;
        }

        var template_file = buffer.toString("utf8", 0, buffer.length);
        var template_html = _.template(template_file);

        var template_data = {
          client: outputs.client,
          invoice_number: args.options.invoicenumber,
          generated_name: output_name,
          generated_date: moment().format('Do MMMM, YYYY'),
          hours_rows: outputs.html,
          hours_total: outputs.totals.hours,
          money_total: outputs.totals.money,
        };

        if (user !== undefined){
          template_data.user = user;
          template_data.currency = user.currency;
        }

        template_data.currency = args.options.currency || '$';
        template_data.extra = args.options.extra || '';

        var output_html = template_html(template_data);
        // Save HTML file
        if (_.indexOf(args.options.format, 'html') > -1) {
          var saveFile = SaveFile(fs, 'output/' + output_name + '.html', output_html);
        }

        // Save PDF file
        Conjuror.castHTMLToPDF(output_html, output_name);
      }, function(err) {
        console.log("Failed to find template.");
      });
  }
};

Conjuror.castHTMLToPDF = function(output_html, output_name){
  if (_.indexOf(args.options.format, 'pdf') > -1) {
    console.log('Saving as a PDF');
    wkhtmltopdf(output_html, {
      pageSize: 'letter',
      output: 'output/' + output_name + '.pdf'
    });
  }
};


// Load Data & Parse
Conjuror.Twirl = function(path, resource, client_path, callback) {
  var resource_file = path + '/' + resource.path;
<commit_msg>added app_path for more robust paths<commit_after>var Promise = require('es6-promise').Promise;
var cheerio = require("cheerio");
var fs      = require("fs");
var _       = require('underscore');
var moment  = require('moment');
var net     = require('net');
var path    = require('path');
var repl    = require('repl');
var csv     = require('csv');
var wkhtmltopdf   = require('wkhtmltopdf');
var config        = require('./lib/conjuror.config.js')
var argv          = require('./lib/conjuror.options.js');

// Run the imported options.
var args = argv.run();

// File Manipulation
var SaveFile = require('./lib/save_file');

// Conjuror
var Conjuror = require('./lib/conjuror.prepareRecipe.js');

// Load Conjuror Modules
Conjuror.Date = require('./lib/conjuror.date.js');
Conjuror.Trim = require('./lib/conjuror.trim.js');
Conjuror.Search = require('./lib/conjuror.search.js');

// App path
var app_path = __filename.replace('conjuror.js', '')

Conjuror.getClient = function(client_file, trim, callback) {
  // Takes as input a client.json file, the client slug to trim by,
  // the callback function which acts on the client object.
  Conjuror.readData(client_file, function(data) {
    // TODO: We need *way* better error handling here.
    if (data.data !== undefined && trim !== undefined) {

      data.data.splice(0,1);
      clientData = data.data;
      var foundClient = _.find(clientData, function(line, index){
        if (line) {

          var lineItem = Conjuror.murmurLineToSchema(line, data.schema);
          return lineItem.slug.toString() === trim.toString();
        } else {
          return false;
        }
      });

      return callback(Conjuror.murmurLineToSchema(foundClient, data.schema));
    } else {
      return callback(undefined);
    }
  });
}


Conjuror.summonUser = function(callback){
  // TODO: We should probably remove the depency on args here, and pass it in
  // as a variable.
  if (args.config !== undefined && args.config.user !== undefined){
    return callback(args.config.user);
  } else {
    return callback({'exists': false});
  }
};

Conjuror.magickData = function(data, schema, date) {

  var outputs = {
    totals: {
      hours: 0,
      money: 0.00,
    },
    csv: _.pluck(schema.fields, 'name').join(','),
    cli: '',
    html: ''
  };
  // Filter by Date / Type
  var date_filter = 'none';

  if (date !== undefined) {

    // TODO: would it be easier to just use moment.js for this?
    var is_date_full        = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
    var is_date_year_month  = /[0-9]{4}-[0-9]{2}/;
    var is_date_month_day   = /[0-9]{2}-[0-9]{2}/;
    var is_date_year        = /[0-9]{4}/;
    var is_week             = /week/;
    var is_month            = /month/;
    var is_today            = /today/;

    if (date.indexOf(':to:') > -1) {
      date_filter = 'range';
    }
    else if (is_week.exec(date)) {
      date_filter = 'this_week';
    } else if (is_today.exec(date)) {
      date_filter = 'today';
    } else if (is_month.exec(date)) {
      date_filter = 'this_month';
    } else if (is_date_full.exec(date)) {
      date_filter = 'full';
    } else if (is_date_year_month.exec(date)) {
      date_filter = 'year_month';
    } else if (is_date_month_day.exec(date)) {
      date_filter = 'month_day';
    } else if (is_date_year.exec(date)) {
      date_filter = 'year';
    } else {
      date_filter = 'month';
    }

    console.log('Filter by ' + date_filter + ': ' + date);
  }

  // Make Totals
  var increment_output = function(item) {

    outputs.totals.hours += item.time;
    outputs.totals.money += (item.time * item.rate);

    // CLI format
    outputs.cli += item.date + '\t ' + item.time + ' \t' + item.client + ' \t' + item.description + '\n';

    // HTML format
    if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {
      outputs.html += '<tr>\n';
      outputs.html += '   <td class="width-50">' + item.date + '</td>\n';
      outputs.html += '   <td class="width-50">' + item.client + '</td>\n';
      outputs.html += '   <td class="text-right">' + item.time + '</td>\n';
      outputs.html += '   <td class="text-left"> hrs</td>\n';
      outputs.html += '   <td>' + item.description + '</td>\n';
      outputs.html += '</tr>\n';
    }
  };

  _.each(data, function(line, index) {

    if (index !== 0){ // skip the first line
      var parts = line;

      // Filter Date & Trim
      var check_date    = Conjuror.Date[date_filter](parts[0], date);
      var check_trim    = Conjuror.Trim(args.options.trim, parts);
      var check_search  = Conjuror.Search(parts, args.options.search);

      // Does Item Meet Filter (date, trim)
      if (_.indexOf([check_date, check_trim, check_search], false) === -1) {

        // Build CSV format (FIXME: nasty code organization going on here)
        if (_.indexOf(args.options.format, 'csv') > -1) {
          outputs.csv += '\n' + line.join(',');
        }

        var item_output = Conjuror.murmurLineToSchema(line, schema);
        increment_output(item_output);
      }
    }
  });

  // Outputs
  outputs.totals.hours = outputs.totals.hours.toFixed(2);
  outputs.totals.money = outputs.totals.money.toFixed(2);
  return outputs;
}


Conjuror.castToCSV = function(outputs) {

  if (_.indexOf(args.options.format, 'csv') > -1) {

    // File Name
    var output_name = 'Conjuror Output - ' + moment().format('D MMMM YYYY');
    if (args.options.output) {
      output_name = args.options.output;
    }

    // Save CSV
    var saveFile = SaveFile(fs, app_path + 'output/' + output_name + '.csv', outputs.csv);
  }
}


// Output HTML
Conjuror.castToHTML = function(outputs, user){

  if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {

    // Get HTML Template
    var template = args.config.invoice_template || 'invoice';
    var template_path = app_path + 'templates/' + template + '.html';

    Conjuror.readManuscript(template_path)

      .then(function(buffer) {
        console.log("Conjuror loaded template");
        var output_name = 'Invoice - ' + moment().format('D MMMM YYYY');

        if (outputs.client) {
          output_name = args.options.invoicenumber + ' - ' +
            outputs.client.name + ' - ' +
            moment().format('DD.MM.YYYY');
        } else if (args.options.output) {
          output_name = args.options.output;
        }

        var template_file = buffer.toString("utf8", 0, buffer.length);
        var template_html = _.template(template_file);

        var template_data = {
          client: outputs.client,
          invoice_number: args.options.invoicenumber,
          generated_name: output_name,
          generated_date: moment().format('Do MMMM, YYYY'),
          hours_rows: outputs.html,
          hours_total: outputs.totals.hours,
          money_total: outputs.totals.money,
        };

        if (user !== undefined){
          template_data.user = user;
          template_data.currency = user.currency;
        }

        template_data.currency = args.options.currency || '$';
        template_data.extra = args.options.extra || '';

        var output_html = template_html(template_data);
        // Save HTML file
        if (_.indexOf(args.options.format, 'html') > -1) {
          var saveFile = SaveFile(fs, app_path + 'output/' + output_name + '.html', output_html);
        }

        // Save PDF file
        Conjuror.castHTMLToPDF(output_html, output_name);
      }, function(err) {
        console.log("Failed to find template.");
      });
  }
};

Conjuror.castHTMLToPDF = function(output_html, output_name){
  if (_.indexOf(args.options.format, 'pdf') > -1) {
    console.log('Saving as a PDF');
    wkhtmltopdf(output_html, {
      pageSize: 'letter',
      output: app_path + 'output/' + output_name + '.pdf'
    });
  }
};


// Load Data & Parse
Conjuror.Twirl = function(path, resource, client_path, callback) {
  var resource_file = path + '/' + resource.path;

{'fraction_non_alphanumeric': 0.16107043675839436, 'fraction_numerical': 0.006437768240343348, 'mean_word_length': 2.524243772241993, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '3650957', 'n_tokens_mistral': 5363, 'n_tokens_neox': 5154, 'n_words': 1124}