<commit_before>"use strict";
/**
 * LCBO API Data Loader
 *
 * This loader is written to do full loads of the LCBOAPI.COM nightly datasets.
 * These datasets are in CSV format, the following loader is responsible for
 *  - Downloading a new dataset archive
 *  - Expanding the contents of the archives
 *  - Parsing the CSV Files and loading each record into MongoDB
 *
 * Created by Andre LeFort on 2015-04-10.
 */

// Some bootstrapping
var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    models = require('../models/models.js'),
    http = require('http-request'),
    fs = require('fs'),
    zip = require('decompress-zip'),
    csv = require('ya-csv'),
    events = require('events'),
    eventEmitter = new events.EventEmitter(),
    ini = require('ini'),
    config = require('get-config').sync(__dirname + '/config');

var eventController = {
    init: function(){
        /* Lets setup the events we care about */
        eventEmitter.on('download_data', lcboLoader.getDatasetsZip);
        eventEmitter.on('extract_data', lcboLoader.extractDatasetsZip);
        eventEmitter.on('load_dataset', lcboLoader.loadDataset);
        eventEmitter.on('load_stores', lcboLoader.loadStores);
        eventEmitter.on('load_products', lcboLoader.loadProducts);
        eventEmitter.on('load_inventories', lcboLoader.loadInventories);
        eventEmitter.on('exit', lcboLoader.terminate);
        eventEmitter.on('load_dataset_complete', lcboLoader.datasetLoaded);
        eventEmitter.on('start_dataload', lcboLoader.loadStores);
    }
};

var lcboLoader = {
    init: function(){
        lcboLoader.mongo.db = mongoose.connect('mongodb://' + lcboLoader.config.mongo.host + '/' + lcboLoader.config.mongo.db);
        lcboLoader.log('Successfully connected to Mongo server ' + mongoose.connection.host + ' on port ' + mongoose.connection.port);
        mongoose.set('debug', false);
    },
    log: function(message){
        console.log(lcboLoader.getDate() + ':  ' + message);
    },
    error: function(message){
        console.error(lcboLoader.getDate() + ':  ' + message);
    },
    getDate: function(){
      return new Date().toString();
    },
    config: {
        url: 'lcboapi.com',
        per_page: 100,
        first_page: 1,
        headers: {
            'Authorization': 'Token MDphNWJlOWVlNi1kZmU2LTExZTQtOTI0Mi1iYmJjODAzYmFiMWY6akFwYTA1MG5RbHFXb3RNSG03NUE2ZUNjVnZFT3Rick9CNDNH'
        },
        mongo: {
            host: '127.0.0.1:27017',
            db: 'lcbo'
        }
    },
    mongo: {
        store: {
            model: mongoose.model('store', models.schema.store)
        },
        product: {
            model: mongoose.model('product', models.schema.product)
        },
        inventory: {
            model: mongoose.model('inventory', models.schema.inventory)
        },
        inventory_archive: {
            model: mongoose.model('archive', models.schema.inventory_archive)
        },
        db: ''
    },
    terminate: function(){
        lcboLoader.log('Terminating data load process.');
        process.exit(0);
    },
    make_url_friendly_string: function(to_convert){
        var url_friendly_string = to_convert.replace(/[^a-z0-9]/gi, '-').toLowerCase();
        url_friendly_string = url_friendly_string.replace('--','-');

        return url_friendly_string;
    },
    processStoreRecord: function(storeRecord){
        var doc = lcboLoader.mongo.store.model(storeRecord);
        doc.location = {};
        doc.location.latitude = doc.latitude;
        doc.location.longitude = doc.longitude;
        doc.url_friendly_name = lcboLoader.make_url_friendly_string(doc.name + '-' + doc.city);

        doc.save(function(error, doc) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            return;
        });
    },
    processProductRecord: function(productRecord){
        var doc = lcboLoader.mongo.product.model(productRecord);

        doc.url_friendly_name = lcboLoader.make_url_friendly_string(doc.name + '-' + doc.id);

        doc.save(function(error, doc) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            return;
        });
    },
    processInventoryRecord: function(inventoryRecord){
        var doc = lcboLoader.mongo.inventory.model(inventoryRecord);

        doc.save(function(error, doc) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            return;
        });
    },
    getDatasetsZip: function(){
        var options = {url: 'http://' + lcboLoader.config.url + '/datasets/latest.zip'};
        lcboLoader.log('Downloading file: ' + options.url);

        http.get(options, config.loader.datapath + '/latest.zip', function (error, result) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            eventEmitter.emit('extract_data');
        });
    },
    extractDatasetsZip: function(){
        var unzipper = new zip(config.loader.datapath + '/latest.zip')

        unzipper.on('error', function (error) {
            lcboLoader.error('Error extracting zip: ' + error);
            process.exit(1);
        });

        unzipper.on('extract', function (log) {
            lcboLoader.log('Finished extracting files from archive.');

            /* Let's emit the next event in the chain */

            eventEmitter.emit('start_dataload');
            return;
        });

        unzipper.on('progress', function (fileIndex, fileCount) {
            /* */
        });

        unzipper.extract({
            path: config.loader.datapath,
            filter: function (file) {
                return file.type !== "SymbolicLink";
            }
        });
    },
    convertTsFs: function(record){
        for(var key in record){
            if(record[key] == 't'){
                record[key] = true;
            }else if(record[key] == 'f'){
                record[key] = false;
            }
        }

        return record;
    },
    uppercaseData: function(record){
        if(record.varietal != undefined && record.varietal != ''){
            var varietal = record.varietal.replace(/\(([^)]+)\)/, function(_, style) { return '(' + style.toUpperCase() + ')'; });

            record.varietal = varietal;
        }

        return record;
    },
    loadDataset: function(schema, schemaName, fileName, deleteQuery){
        /* First let's wipe the collection clean */
        schema.remove(deleteQuery, function(error){
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            var reader = csv.createCsvFileReader(fileName, {columnsFromHeader: true});

            reader.addListener('data', function(data) {
                var record = lcboLoader.convertTsFs(data);
                record = lcboLoader.uppercaseData(data);

                /*
                No need to process records that are dead
                 */
                if(record.is_dead == true){
                    return;
                }

                if(schemaName == 'stores'){
                    lcboLoader.processStoreRecord(record);
                }else if (schemaName == 'products'){
                    lcboLoader.processProductRecord(record);
                }else if(schemaName == 'inventories') {
                    lcboLoader.processInventoryRecord(record);
                }
            });

            reader.addListener('end', function(data) {
                lcboLoader.log('Finished loading data for "' + schemaName + '" from ' + fileName);
                /*
                Lets let the handler know we're done this file
                 */
                eventEmitter.emit('load_dataset_complete', schemaName);
            });
        });
    },
    loadStores: function(){
        eventEmitter.emit('load_dataset', lcboLoader.mongo.store.model, 'stores', config.loader.datapath + '/stores.csv', {});
    },
    loadProducts: function(){
        eventEmitter.emit('load_dataset', lcboLoader.mongo.product.model, 'products', config.loader.datapath + '/products.csv', {});
    },
    loadInventories: function(){
        // Need to copy the data over to inventories archive collection before pruning and loading below
        var oneMonthAgo = new Date();
        oneMonthAgo.setDate(oneMonthAgo.getDate() -30);

        var date_query = {updated_at: {$lt: oneMonthAgo}};
        // Prune the archives to only keep one month of data
        var model = lcboLoader.mongo.inventory_archive.model;
        model.remove(date_query, function(error){
            lcboLoader.mongo.inventory.model.find({}).exec(function(err, docs){
                for (var i = 0; i < docs.length; i++) {
                    var archived_inventory = {};
                    archived_inventory.product_id = docs[i].product_id;
                    archived_inventory.store_id = docs[i].store_id;
                    archived_inventory.is_dead = docs[i].is_dead;
                    archived_inventory.quantity = docs[i].quantity;
                    archived_inventory.reported_on = docs[i].reported_on;
                    archived_inventory.updated_at = docs[i].updated_at;

                    var doc = lcboLoader.mongo.inventory_archive.model(archived_inventory);

                    doc.save(function(error, doc) {
                        if (error) {
                            lcboLoader.error(error);
                            process.exit(1);
                        }

                        return;
                    });
                }
                eventEmitter.emit('load_dataset', lcboLoader.mongo.inventory.model, 'inventories', config.loader.datapath + '/inventories.csv', {});
            });
        });
    },
    datasetLoaded: function(schemaName){
        switch(schemaName){
            case 'stores':
                lcboLoader.loadProducts();
                break;
            case 'products':
                lcboLoader.loadInventories();
                break;
            case 'inventories':
                eventEmitter.emit('exit');
                break;
        }
    }
};
/* Final Bootstrap */
eventController.init();
lcboLoader.init();
/* Now, let's kick off the work */
eventEmitter.emit('download_data');
//lcboLoader.loadStores();<commit_msg>mongoose debug off<commit_after>"use strict";
/**
 * LCBO API Data Loader
 *
 * This loader is written to do full loads of the LCBOAPI.COM nightly datasets.
 * These datasets are in CSV format, the following loader is responsible for
 *  - Downloading a new dataset archive
 *  - Expanding the contents of the archives
 *  - Parsing the CSV Files and loading each record into MongoDB
 *
 * Created by Andre LeFort on 2015-04-10.
 */

// Some bootstrapping
var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    models = require('../models/models.js'),
    http = require('http-request'),
    fs = require('fs'),
    zip = require('decompress-zip'),
    csv = require('ya-csv'),
    events = require('events'),
    eventEmitter = new events.EventEmitter(),
    ini = require('ini'),
    config = require('get-config').sync(__dirname + '/config');

var eventController = {
    init: function(){
        /* Lets setup the events we care about */
        eventEmitter.on('download_data', lcboLoader.getDatasetsZip);
        eventEmitter.on('extract_data', lcboLoader.extractDatasetsZip);
        eventEmitter.on('load_dataset', lcboLoader.loadDataset);
        eventEmitter.on('load_stores', lcboLoader.loadStores);
        eventEmitter.on('load_products', lcboLoader.loadProducts);
        eventEmitter.on('load_inventories', lcboLoader.loadInventories);
        eventEmitter.on('exit', lcboLoader.terminate);
        eventEmitter.on('load_dataset_complete', lcboLoader.datasetLoaded);
        eventEmitter.on('start_dataload', lcboLoader.loadStores);
    }
};

var lcboLoader = {
    init: function(){
        lcboLoader.mongo.db = mongoose.connect('mongodb://' + lcboLoader.config.mongo.host + '/' + lcboLoader.config.mongo.db);
        lcboLoader.log('Successfully connected to Mongo server ' + mongoose.connection.host + ' on port ' + mongoose.connection.port);
        mongoose.set('debug', false);
    },
    log: function(message){
        console.log(lcboLoader.getDate() + ':  ' + message);
    },
    error: function(message){
        console.error(lcboLoader.getDate() + ':  ' + message);
    },
    getDate: function(){
      return new Date().toString();
    },
    config: {
        url: 'lcboapi.com',
        per_page: 100,
        first_page: 1,
        headers: {
            'Authorization': 'Token MDphNWJlOWVlNi1kZmU2LTExZTQtOTI0Mi1iYmJjODAzYmFiMWY6akFwYTA1MG5RbHFXb3RNSG03NUE2ZUNjVnZFT3Rick9CNDNH'
        },
        mongo: {
            host: '127.0.0.1:27017',
            db: 'lcbo'
        }
    },
    mongo: {
        store: {
            model: mongoose.model('store', models.schema.store)
        },
        product: {
            model: mongoose.model('product', models.schema.product)
        },
        inventory: {
            model: mongoose.model('inventory', models.schema.inventory)
        },
        inventory_archive: {
            model: mongoose.model('archive', models.schema.inventory_archive)
        },
        db: ''
    },
    terminate: function(){
        lcboLoader.log('Terminating data load process.');
        process.exit(0);
    },
    make_url_friendly_string: function(to_convert){
        var url_friendly_string = to_convert.replace(/[^a-z0-9]/gi, '-').toLowerCase();
        url_friendly_string = url_friendly_string.replace('--','-');

        return url_friendly_string;
    },
    processStoreRecord: function(storeRecord){
        var doc = lcboLoader.mongo.store.model(storeRecord);
        doc.location = {};
        doc.location.latitude = doc.latitude;
        doc.location.longitude = doc.longitude;
        doc.url_friendly_name = lcboLoader.make_url_friendly_string(doc.name + '-' + doc.city);

        doc.save(function(error, doc) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            return;
        });
    },
    processProductRecord: function(productRecord){
        var doc = lcboLoader.mongo.product.model(productRecord);

        doc.url_friendly_name = lcboLoader.make_url_friendly_string(doc.name + '-' + doc.id);

        doc.save(function(error, doc) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            return;
        });
    },
    processInventoryRecord: function(inventoryRecord){
        var doc = lcboLoader.mongo.inventory.model(inventoryRecord);

        doc.save(function(error, doc) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            return;
        });
    },
    getDatasetsZip: function(){
        var options = {url: 'http://' + lcboLoader.config.url + '/datasets/latest.zip'};
        lcboLoader.log('Downloading file: ' + options.url);

        http.get(options, config.loader.datapath + '/latest.zip', function (error, result) {
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            eventEmitter.emit('extract_data');
        });
    },
    extractDatasetsZip: function(){
        var unzipper = new zip(config.loader.datapath + '/latest.zip')

        unzipper.on('error', function (error) {
            lcboLoader.error('Error extracting zip: ' + error);
            process.exit(1);
        });

        unzipper.on('extract', function (log) {
            lcboLoader.log('Finished extracting files from archive.');

            /* Let's emit the next event in the chain */

            eventEmitter.emit('start_dataload');
            return;
        });

        unzipper.on('progress', function (fileIndex, fileCount) {
            /* */
        });

        unzipper.extract({
            path: config.loader.datapath,
            filter: function (file) {
                return file.type !== "SymbolicLink";
            }
        });
    },
    convertTsFs: function(record){
        for(var key in record){
            if(record[key] == 't'){
                record[key] = true;
            }else if(record[key] == 'f'){
                record[key] = false;
            }
        }

        return record;
    },
    uppercaseData: function(record){
        if(record.varietal != undefined && record.varietal != ''){
            var varietal = record.varietal.replace(/\(([^)]+)\)/, function(_, style) { return '(' + style.toUpperCase() + ')'; });

            record.varietal = varietal;
        }

        return record;
    },
    loadDataset: function(schema, schemaName, fileName, deleteQuery){
        /* First let's wipe the collection clean */
        schema.remove(deleteQuery, function(error){
            if (error) {
                lcboLoader.error(error);
                process.exit(1);
            }

            var reader = csv.createCsvFileReader(fileName, {columnsFromHeader: true});

            reader.addListener('data', function(data) {
                var record = lcboLoader.convertTsFs(data);
                record = lcboLoader.uppercaseData(data);

                /*
                No need to process records that are dead
                 */
                if(record.is_dead == true){
                    return;
                }

                if(schemaName == 'stores'){
                    lcboLoader.processStoreRecord(record);
                }else if (schemaName == 'products'){
                    lcboLoader.processProductRecord(record);
                }else if(schemaName == 'inventories') {
                    lcboLoader.processInventoryRecord(record);
                }
            });

            reader.addListener('end', function(data) {
                lcboLoader.log('Finished loading data for "' + schemaName + '" from ' + fileName);
                /*
                Lets let the handler know we're done this file
                 */
                eventEmitter.emit('load_dataset_complete', schemaName);
            });
        });
    },
    loadStores: function(){
        eventEmitter.emit('load_dataset', lcboLoader.mongo.store.model, 'stores', config.loader.datapath + '/stores.csv', {});
    },
    loadProducts: function(){
        eventEmitter.emit('load_dataset', lcboLoader.mongo.product.model, 'products', config.loader.datapath + '/products.csv', {});
    },
    loadInventories: function(){
        // Need to copy the data over to inventories archive collection before pruning and loading below
        var oneMonthAgo = new Date();
        oneMonthAgo.setDate(oneMonthAgo.getDate() -30);

        var date_query = {updated_at: {$lt: oneMonthAgo}};
        // Prune the archives to only keep one month of data
        var model = lcboLoader.mongo.inventory_archive.model;
        model.remove(date_query, function(error){
            lcboLoader.mongo.inventory.model.find({}).exec(function(err, docs){
                console.log('# of inventory records: ' + docs.length);
                for (var i = 0; i < docs.length; i++) {
                    var archived_inventory = {};
                    archived_inventory.product_id = docs[i].product_id;
                    archived_inventory.store_id = docs[i].store_id;
                    archived_inventory.is_dead = docs[i].is_dead;
                    archived_inventory.quantity = docs[i].quantity;
                    archived_inventory.reported_on = docs[i].reported_on;
                    archived_inventory.updated_at = docs[i].updated_at;

                    var doc = lcboLoader.mongo.inventory_archive.model(archived_inventory);

                    doc.save(function(error, doc) {
                        if (error) {
                            lcboLoader.error(error);
                            process.exit(1);
                        }

                        return;
                    });
                }
                eventEmitter.emit('load_dataset', lcboLoader.mongo.inventory.model, 'inventories', config.loader.datapath + '/inventories.csv', {});
            });
        });
    },
    datasetLoaded: function(schemaName){
        switch(schemaName){
            case 'stores':
                lcboLoader.loadProducts();
                break;
            case 'products':
                lcboLoader.loadInventories();
                break;
            case 'inventories':
                eventEmitter.emit('exit');
                break;
        }
    }
};
/* Final Bootstrap */
eventController.init();
lcboLoader.init();
/* Now, let's kick off the work */
eventEmitter.emit('download_data');
//lcboLoader.loadStores();<|endoftext|>


<commit_before>'use strict';

import menuFactory from '../menuFactory';

let styles = {

  menuWrap(isOpen) {
    return {
      position: 'fixed',
      zIndex: 2,
      width: 300,
      height: '100%',
      WebkitTransform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(0, -100%, 0)',
      transform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(0, -100%, 0)',
      WebkitTransition: 'all 0.5s ease-in-out',
      transition: 'all 0.5s ease-in-out'
    };
  },

  menu() {
    return {
      height: '100%'
    };
  },

  item() {
    return {
      display: 'block',
      outline: 'none'
    };
  },

  overlay(isOpen) {
    return {
      position: 'fixed',
      width: '100%',
      height: '100%',
      background: 'rgba(0, 0, 0, 0.3)',
      opacity: isOpen ? 1 : 0,
      WebkitTransform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(-100%, 0, 0)',
      transform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(-100%, 0, 0)',
      WebkitTransition: isOpen ? 'opacity 0.5s' : 'opacity 0.5s, transform 0.1s 0.5s',
      transition: isOpen ? 'opacity 0.5s' : 'opacity 0.5s, transform 0.1s 0.5s',
      zIndex: 1
    };
  },

  pageWrap(isOpen) {
    return {
      WebkitTransform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(300px, 0, 0)',
      transform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(300px, 0, 0)',
      WebkitTransition: 'all 0.5s',
      transition: 'all 0.5s'
    };
  },

  outerContainer(isOpen) {
    return {
      perspective: '1500px',
      perspectiveOrigin: '0% 50%',
      overflow: isOpen ? '' : 'hidden'
    };
  }
};

export default menuFactory(styles);
<commit_msg>Fix fallDown<commit_after>'use strict';

import menuFactory from '../menuFactory';

let styles = {

  menuWrap(isOpen) {
    return {
      position: 'fixed',
      zIndex: 2,
      width: 300,
      height: '100%',
      WebkitTransform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(0, -100%, 0)',
      transform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(0, -100%, 0)',
      WebkitTransition: 'all 0.5s ease-in-out',
      transition: 'all 0.5s ease-in-out'
    };
  },

  menu() {
    return {
      height: '100%'
    };
  },

  item() {
    return {
      display: 'block',
      outline: 'none'
    };
  },

  overlay(isOpen) {
    return {
      position: 'fixed',
      width: '100%',
      height: '100%',
      background: 'rgba(0, 0, 0, 0.3)',
      opacity: isOpen ? 1 : 0,
      WebkitTransform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(-100%, 0, 0)',
      transform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(-100%, 0, 0)',
      WebkitTransition: isOpen ? 'opacity 0.5s' : 'opacity 0.5s, transform 0.1s 0.5s',
      transition: isOpen ? 'opacity 0.5s' : 'opacity 0.5s, transform 0.1s 0.5s',
      zIndex: 1
    };
  },

  pageWrap(isOpen) {
    return {
      WebkitTransform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(300px, 0, 0)',
      transform: isOpen ? 'translate3d(0, 0, 0)' : 'translate3d(300px, 0, 0)',
      WebkitTransition: 'all 0.5s',
      transition: 'all 0.5s'
    };
  },

  outerContainer(isOpen) {
    return {
      WebkitPerspective: '1500px',
      perspective: '1500px',
      WebkitPerspectiveOrigin: '0% 50%',
      perspectiveOrigin: '0% 50%',
      overflow: isOpen ? '' : 'hidden'
    };
  }
};

export default menuFactory(styles);
<|endoftext|>


<commit_before>'use strict';
// Find conversion rate between viewing the teacher sign up form and creating a class

// Rough algorithm
// 1. Find all users that saw teacher form between 7 and 14 days ago
// 2. Find subset of those users that also created a class in last 14 days

// NOTE: update the endDay variable below to control the date range
// NOTE: assumes events happened in correct order, and can't be skipped somehow

// TODO: users that saw teacher form earlier in date range have more time to create a class

if (process.argv.length !== 3) {
  console.log("Usage: node <script> <mongo connection Url analytics>");
  console.log("Include ?readPreference=secondary in connection URLs");
  process.exit();
}

const scriptStartTime = new Date();
const co = require('co');
const MongoClient = require('mongodb').MongoClient;
const mongoose = require('mongoose');
const mongoConnUrlAnalytics = process.argv[2];

const debugOutput = true;
const daysToViewForm = 7;
const daysToCreateClass = 7;
const endDay = "2017-02-03";

let startDay = new Date(`${endDay}T00:00:00.000Z`);
startDay.setUTCDate(startDay.getUTCDate() - daysToViewForm - daysToCreateClass);
startDay = startDay.toISOString().substring(0, 10);
debug(`Measuring days ${startDay} to ${endDay}`);

const startEvents = ['Teachers Request Demo Loaded', 'Teachers Create Account Loaded', 'Teachers Convert Account Loaded'];
const remainingOrderedEvents = ['Teachers Classes Loaded', 'Teachers Classes Create New Class Started', 'Teachers Classes Create New Class Finished'];

co(function*() {
  const analyticsDb = yield MongoClient.connect(mongoConnUrlAnalytics, {connectTimeoutMS: 1000 * 60 * 60, socketTimeoutMS: 1000 * 60 * 60});

  const startObjectId = objectIdWithTimestamp(new Date(`${startDay}T00:00:00.000Z`));
  const formEndDate = new Date(`${endDay}T00:00:00.000Z`);
  formEndDate.setUTCDate(formEndDate.getUTCDate() - daysToCreateClass);
  const midObjectId = objectIdWithTimestamp(formEndDate);
  debug(`Finding view teacher form events between ${startDay} and ${formEndDate.toISOString().substring(0, 10)}...`);

  let query = {$and: [{_id: {$gte: startObjectId}}, {_id: {$lt: midObjectId}}, {event: {$in: startEvents}}]};
  const formEvents = yield analyticsDb.collection('log').find(query, {user: 1}).toArray();
  debug(`Teacher form events found ${formEvents.length}`);
  const userFormMap = {};
  for (const event of formEvents) {
    userFormMap[event.user] = event._id.getTimestamp();
  }
  const userIds = Object.keys(userFormMap);
  debug(`Unique users ${userIds.length}`);

  debug(`Finding rest of create class funnel events between ${startDay} and ${endDay}...`);
  const endObjectId = objectIdWithTimestamp(new Date(`${endDay}T00:00:00.000Z`));
  query = {$and: [{_id: {$gte: startObjectId}}, {_id: {$lt: endObjectId}}, {event: {$in: remainingOrderedEvents}}, {user: {$in: userIds}}]};
  const classEvents = yield analyticsDb.collection('log').find(query, {event: 1, user: 1}).toArray();
  debug(`Class created events found ${classEvents.length}`);
  const userFunnelEventsMap = {};
  for (const event of classEvents) {
    const progressCutoff = new Date(userFormMap[event.user]);
    progressCutoff.setUTCDate(progressCutoff.getUTCDate() + daysToCreateClass);
    if (progressCutoff < event._id.getTimestamp()) {
      // Skip events outside of timeframe to create class
      continue;
    }
    if (!userFunnelEventsMap[event.event]) userFunnelEventsMap[event.event] = {};
    userFunnelEventsMap[event.event][event.user] = true;
  }

  const usersSawTeacherForm = userIds.length;
  let teachersCreatedClass = 0;
  console.log(`Looking at events from ${startDay} to ${endDay}:`);
  console.log(`100% ${usersSawTeacherForm} users saw teacher form`);
  for (const event of remainingOrderedEvents) {
    const currentEventCount = Object.keys(userFunnelEventsMap[event] || {}).length;
    console.log(`${(currentEventCount * 100.0 / usersSawTeacherForm).toFixed(2)}% ${currentEventCount} users saw event ${event}`);
    if (event === 'Teachers Classes Create New Class Finished') {
      teachersCreatedClass = currentEventCount;
    }
  }
  console.log(`Saw teacher signup form= ${usersSawTeacherForm} Created class= ${teachersCreatedClass} Conversion= ${teachersCreatedClass / usersSawTeacherForm} ${teachersCreatedClass * 100.0 / usersSawTeacherForm}%`);

  analyticsDb.close();
  debug(`Script runtime: ${new Date() - scriptStartTime}`);
})

// * Helper functions

function debug(msg) {
  if (debugOutput) console.log(`${new Date().toISOString()} ${msg}`);
}

function objectIdWithTimestamp(timestamp) {
  // Convert string date to Date object (otherwise assume timestamp is a date)
  if (typeof(timestamp) == 'string') timestamp = new Date(timestamp);
  // Convert date object to hex seconds since Unix epoch
  var hexSeconds = Math.floor(timestamp/1000).toString(16);
  // Create an ObjectId with that hex timestamp
  var constructedObjectId = mongoose.Types.ObjectId(hexSeconds + "0000000000000000");
  return constructedObjectId;
}
<commit_msg>Update teacher signup conversion script<commit_after>'use strict';
// Find conversion rate between viewing the teacher sign up form and creating a class

// Rough algorithm
// 1. Find all users that saw teacher form between 7 and 14 days ago
// 2. Find subset of those users that also created a class in last 14 days

// NOTE: update the endDay variable below to control the date range
// NOTE: assumes events happened in correct order, and can't be skipped somehow

// TODO: users that saw teacher form earlier in date range have more time to create a class

if (process.argv.length !== 3) {
  console.log("Usage: node <script> <mongo connection Url analytics>");
  console.log("Include ?readPreference=secondary in connection URLs");
  process.exit();
}

const scriptStartTime = new Date();
const co = require('co');
const MongoClient = require('mongodb').MongoClient;
const mongoose = require('mongoose');
const mongoConnUrlAnalytics = process.argv[2];

const debugOutput = true;
const daysToViewForm = 7;
const daysToCreateClass = 7;
const endDay = "2017-09-11";

let startDay = new Date(`${endDay}T00:00:00.000Z`);
startDay.setUTCDate(startDay.getUTCDate() - daysToViewForm - daysToCreateClass);
startDay = startDay.toISOString().substring(0, 10);
debug(`Measuring days ${startDay} to ${endDay}`);

const startEventValues = ['Teachers Request Demo Loaded', 'Teachers Create Account Loaded', 'Teachers Convert Account Loaded', 'Homepage Click Teacher Button CTA'];
const remainingOrderedEventValues = [
  'CreateAccountModal Teacher BasicInfoView Submit Clicked',
  'CreateAccountModal Teacher BasicInfoView Submit Success',
  'CreateAccountModal Teacher SchoolInfoPanel Continue Clicked',
  'CreateAccountModal Teacher SchoolInfoPanel Continue Success',
  'CreateAccountModal Teacher TeacherRolePanel Continue Clicked',
  'CreateAccountModal Teacher TeacherRolePanel Continue Success',
  'CreateAccountModal Teacher DemographicsPanel Signup Clicked',
  'CreateAccountModal Teacher DemographicsPanel Signup Success',
  // 'Teachers Classes Loaded',
  // 'Teachers Classes Create New Class Started',
  // 'Teachers Classes Create New Class Finished'
];

co(function*() {
  const analyticsDb = yield MongoClient.connect(mongoConnUrlAnalytics, {connectTimeoutMS: 1000 * 60 * 60, socketTimeoutMS: 1000 * 60 * 60});

  const startObjectId = objectIdWithTimestamp(new Date(`${startDay}T00:00:00.000Z`));
  const formEndDate = new Date(`${endDay}T00:00:00.000Z`);
  formEndDate.setUTCDate(formEndDate.getUTCDate() - daysToCreateClass);
  const midObjectId = objectIdWithTimestamp(formEndDate);
  debug(`Finding view teacher form events between ${startDay} and ${formEndDate.toISOString().substring(0, 10)}...`);

  let query = {$and: [{_id: {$gte: startObjectId}}, {_id: {$lt: midObjectId}}, {event: {$in: startEventValues}}]};
  const startEvents = yield analyticsDb.collection('log').find(query, {user: 1}).toArray();
  debug(`Teacher form events found ${startEvents.length}`);
  const userFormMap = {};
  for (const event of startEvents) {
    userFormMap[event.user] = event._id.getTimestamp();
  }
  const userIds = Object.keys(userFormMap);
  debug(`Unique users ${userIds.length}`);

  debug(`Finding rest of create class funnel events between ${startDay} and ${endDay}...`);
  const endObjectId = objectIdWithTimestamp(new Date(`${endDay}T00:00:00.000Z`));

  query = {$and: [{_id: {$gte: startObjectId}}, {_id: {$lt: endObjectId}}, {event: {$in: remainingOrderedEventValues}}, {user: {$in: userIds}}]};
  const classEvents = yield analyticsDb.collection('log').find(query, {event: 1, user: 1}).toArray();
  debug(`Class created events found ${classEvents.length}`);
  const userFunnelEventsMap = {};
  for (const event of classEvents) {
    const progressCutoff = new Date(userFormMap[event.user]);
    progressCutoff.setUTCDate(progressCutoff.getUTCDate() + daysToCreateClass);
    if (progressCutoff < event._id.getTimestamp()) {
      // Skip events outside of timeframe to create class
      continue;
    }
    if (!userFunnelEventsMap[event.event]) userFunnelEventsMap[event.event] = {};
    userFunnelEventsMap[event.event][event.user] = true;
  }

  const usersSawTeacherForm = userIds.length;
  let teachersCreatedClass = 0;
  debug(`Looking at events from ${startDay} to ${endDay}:`);
  console.log(`100% ${usersSawTeacherForm} users saw teacher form`);
  for (const event of remainingOrderedEventValues) {
    const currentEventCount = Object.keys(userFunnelEventsMap[event] || {}).length;
    console.log(`${(currentEventCount * 100.0 / usersSawTeacherForm).toFixed(2)}% ${currentEventCount} users saw event ${event}`);
    if (event === remainingOrderedEventValues[remainingOrderedEventValues.length - 1]) {
      teachersCreatedClass = currentEventCount;
    }
  }
  console.log(`Saw teacher signup form= ${usersSawTeacherForm} Created class= ${teachersCreatedClass} Conversion= ${teachersCreatedClass / usersSawTeacherForm} ${teachersCreatedClass * 100.0 / usersSawTeacherForm}%`);

  analyticsDb.close();
  debug(`Script runtime: ${new Date() - scriptStartTime}`);
})

// * Helper functions

function debug(msg) {
  if (debugOutput) console.log(`${new Date().toISOString()} ${msg}`);
}

function objectIdWithTimestamp(timestamp) {
  // Convert string date to Date object (otherwise assume timestamp is a date)
  if (typeof(timestamp) == 'string') timestamp = new Date(timestamp);
  // Convert date object to hex seconds since Unix epoch
  var hexSeconds = Math.floor(timestamp/1000).toString(16);
  // Create an ObjectId with that hex timestamp
  var constructedObjectId = mongoose.Types.ObjectId(hexSeconds + "0000000000000000");
  return constructedObjectId;
}
<|endoftext|>


<commit_before>/*jshint node:true, mocha:true*/
/**
 * @author lattmann / https://github.com/lattmann
 */

var testFixture = require('../_globals');

describe('Run plugin CLI', function () {
    'use strict';

    var gmeConfig = testFixture.getGmeConfig(),
        logger = testFixture.logger.fork('run_plugin.spec'),
        spawn = testFixture.childProcess.spawn,
        storage,
        expect = testFixture.expect,
        filename = require('path').normalize('src/bin/run_plugin.js'),
        projectName = 'runPluginCLI',
        gmeAuth,
        Q = testFixture.Q;

    before(function (done) {
        //adding some project to the database
        testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName)
            .then(function (gmeAuth_) {
                gmeAuth = gmeAuth_;
                storage = testFixture.getMongoStorage(logger, gmeConfig, gmeAuth);
                return storage.openDatabase();
            })
            .then(function () {
                return testFixture.importProject(storage, {
                    projectSeed: './test/bin/run_plugin/project.json',
                    projectName: projectName,
                    branchName: 'master',
                    gmeConfig: gmeConfig,
                    logger: logger
                });
            })
            .nodeify(done);
    });

    after(function (done) {
        Q.allSettled([
            storage.closeDatabase(),
            gmeAuth.unload()
        ])
        .nodeify(done);
    });

    describe('as a child process', function () {
        it('should run the Minimal Working Example plugin', function (done) {
            var runpluginProcess = spawn('node', [filename, 'MinimalWorkingExample', projectName]),
                stdout,
                stderr;

            runpluginProcess.stdout.on('data', function (data) {
                stdout = stdout || '';
                stdout += data.toString();
                //console.log(data.toString());
            });

            runpluginProcess.stderr.on('data', function (data) {
                stderr = stderr || '';
                stderr += data.toString();
                //console.log(data.toString());
            });

            runpluginProcess.on('close', function (code) {
                //expect(stdout).to.contain('execution was successful');
                expect(stderr).to.contain('This is an error message');
                expect(code).to.equal(0);
                done();
            });
        });
    });

    describe('as a library', function () {
        var runPlugin = require('../../src/bin/run_plugin'),
            oldProcessExit = process.exit;

        afterEach(function () {
            process.exit = oldProcessExit;
        });

        it('should run the Minimal Working Example plugin', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(0);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', projectName],
                function (err, result) {
                    if (err) {
                        done(new Error(err));
                        return;
                    }
                    expect(result.success).to.equal(true);
                    expect(result.error).to.equal(null);
                }
            );
        });

        it('should run the Minimal Working Example plugin and fail with configuration file', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(1);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', projectName,
                    '-j', './test/bin/run_plugin/MinimalWorkingExample.config.json'],
                function (err) {
                    if (err) {
                        expect(err).to.match(/Failed on purpose./);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });

        it('should run the Minimal Working Example plugin if owner is specified', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(0);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', projectName,
                    '-o', gmeConfig.authentication.guestAccount],
                function (err, result) {
                    if (err) {
                        done(new Error(err));
                        return;
                    }
                    expect(result.success).to.equal(true);
                    expect(result.error).to.equal(null);
                }
            );
        });

        it('should fail to run the Minimal Working Example plugin if does not have access to project', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(1);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', 'not_authorized_project'],
                function (err) {
                    if (err) {
                        expect(err).to.match(/Not authorized to read or write project/);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });

        it('should fail to run plugin if no plugin name and no project name is not given', function (done) {
            process.exit = function () {
                done();
            };

            runPlugin.main(['node', filename],
                function (err/*, result*/) {
                    if (err) {
                        expect(err).to.match(/must be specified/);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });

        it('should fail to run plugin if no project name is given', function (done) {
            process.exit = function () {
                done();
            };

            runPlugin.main(['node', filename, projectName],
                function (err) {
                    if (err) {
                        expect(err).to.match(/must be specified/);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });
    });
});<commit_msg>Update run_plugin.spec.js<commit_after>/*jshint node:true, mocha:true*/
/**
 * @author lattmann / https://github.com/lattmann
 */

var testFixture = require('../_globals');

describe('Run plugin CLI', function () {
    'use strict';

    var gmeConfig = testFixture.getGmeConfig(),
        logger = testFixture.logger.fork('run_plugin.spec'),
        spawn = testFixture.childProcess.spawn,
        storage,
        expect = testFixture.expect,
        filename = require('path').normalize('src/bin/run_plugin.js'),
        projectName = 'runPluginCLI',
        gmeAuth,
        Q = testFixture.Q;

    before(function (done) {
        //adding some project to the database
        testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName)
            .then(function (gmeAuth_) {
                gmeAuth = gmeAuth_;
                storage = testFixture.getMongoStorage(logger, gmeConfig, gmeAuth);
                return storage.openDatabase();
            })
            .then(function () {
                return testFixture.importProject(storage, {
                    projectSeed: './test/bin/run_plugin/project.json',
                    projectName: projectName,
                    branchName: 'master',
                    gmeConfig: gmeConfig,
                    logger: logger
                });
            })
            .nodeify(done);
    });

    after(function (done) {
        Q.all([
            storage.closeDatabase(),
            gmeAuth.unload()
        ])
        .nodeify(done);
    });

    describe('as a child process', function () {
        it('should run the Minimal Working Example plugin', function (done) {
            var runpluginProcess = spawn('node', [filename, 'MinimalWorkingExample', projectName]),
                stdout,
                stderr;

            runpluginProcess.stdout.on('data', function (data) {
                stdout = stdout || '';
                stdout += data.toString();
                //console.log(data.toString());
            });

            runpluginProcess.stderr.on('data', function (data) {
                stderr = stderr || '';
                stderr += data.toString();
                //console.log(data.toString());
            });

            runpluginProcess.on('close', function (code) {
                //expect(stdout).to.contain('execution was successful');
                expect(stderr).to.contain('This is an error message');
                expect(code).to.equal(0);
                done();
            });
        });
    });

    describe('as a library', function () {
        var runPlugin = require('../../src/bin/run_plugin'),
            oldProcessExit = process.exit;

        afterEach(function () {
            process.exit = oldProcessExit;
        });

        it('should run the Minimal Working Example plugin', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(0);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', projectName],
                function (err, result) {
                    if (err) {
                        done(new Error(err));
                        return;
                    }
                    expect(result.success).to.equal(true);
                    expect(result.error).to.equal(null);
                }
            );
        });

        it('should run the Minimal Working Example plugin and fail with configuration file', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(1);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', projectName,
                    '-j', './test/bin/run_plugin/MinimalWorkingExample.config.json'],
                function (err) {
                    if (err) {
                        expect(err).to.match(/Failed on purpose./);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });

        it('should run the Minimal Working Example plugin if owner is specified', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(0);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', projectName,
                    '-o', gmeConfig.authentication.guestAccount],
                function (err, result) {
                    if (err) {
                        done(new Error(err));
                        return;
                    }
                    expect(result.success).to.equal(true);
                    expect(result.error).to.equal(null);
                }
            );
        });

        it('should fail to run the Minimal Working Example plugin if does not have access to project', function (done) {
            process.exit = function (code) {
                expect(code).to.equal(1);
                done();
            };

            runPlugin.main(['node', filename, 'MinimalWorkingExample', 'not_authorized_project'],
                function (err) {
                    if (err) {
                        expect(err).to.match(/Not authorized to read or write project/);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });

        it('should fail to run plugin if no plugin name and no project name is not given', function (done) {
            process.exit = function () {
                done();
            };

            runPlugin.main(['node', filename],
                function (err/*, result*/) {
                    if (err) {
                        expect(err).to.match(/must be specified/);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });

        it('should fail to run plugin if no project name is given', function (done) {
            process.exit = function () {
                done();
            };

            runPlugin.main(['node', filename, projectName],
                function (err) {
                    if (err) {
                        expect(err).to.match(/must be specified/);
                        return;
                    }
                    done(new Error('should have failed to run plugin'));
                }
            );
        });
    });
});
<|endoftext|>


<commit_before>$(document).ready(function() {
  fetch('/profile-update')
      .then((response) => response.json())
      .then((userData) => {
        const userName = userData['name'];
        const userInterests = userData['interests'];
        const userSkills = userData['skills'];

        $('#name').val(userName);
        $('#interests').val(userInterests);
        $('#skills').val(userSkills);
      });
});
<commit_msg>Clean up frontend profile editing<commit_after>$(document).ready(function() {
  showCurrentUserInfo();
});

async function showCurrentUserInfo() {
  const response = await fetch('/profile-update');
  const userData = await response.json();

  const userName = userData['name'];
  const userInterests = userData['interests'];
  const userSkills = userData['skills'];

  $('#name').val(userName);
  $('#interests').val(userInterests);
  $('#skills').val(userSkills);
}
<|endoftext|>


<commit_before>var expect       = require("chai").expect;
var Cloudevent   = require("../index.js");

const type   = "com.github.pull.create";
const source = "urn:event:from:myapi/resourse/123";
const time   = new Date();
const schemaurl = "http://example.com/registry/myschema.json";
const contenttype = "application/json";
const data = {};
const extensions = {};

var cloudevent = new Cloudevent()
                       .type(type)
                       .source(source);

describe("CloudEvents Spec 0.1 - JavaScript SDK", () => {

  describe("JSON Format", () => {

    describe("Required context attributes", () => {
      it("requires 'eventType'", () => {
        expect(cloudevent.format()).to.have.property("eventType");
      });

      it("requires 'eventTypeVersion'", () => {
        cloudevent.eventTypeVersion("1.0");
        expect(cloudevent.format()).to.have.property("eventTypeVersion");
      });

      it("requires 'cloudEventsVersion'", () => {
        expect(cloudevent.format()).to.have.property("cloudEventsVersion");
      });

      it("requires 'source'", () => {
        expect(cloudevent.format()).to.have.property("source");
      });

      it("requires 'eventID'", () => {
        expect(cloudevent.format()).to.have.property("eventID");
      });
    });

    describe("Optional context attributes", () => {
      it("contains 'eventTime'", () => {
        cloudevent.time(time);
        expect(cloudevent.format()).to.have.property("eventTime");
      });

      it("contains 'schemaURL'", () => {
        cloudevent.schemaurl(schemaurl);
        expect(cloudevent.format()).to.have.property("schemaURL");
      });

      it("contains 'contentType'", () => {
        cloudevent.contenttype(contenttype);
        expect(cloudevent.format()).to.have.property("contentType");
      });

      it("contains 'data'", () => {
        cloudevent.data(data);
        expect(cloudevent.format()).to.have.property("data");
      });

      it("contains 'extensions'", () => {
        cloudevent.addExtension("foo", "value");
        expect(cloudevent.format()).to.have.property("extensions");
      });

      it("'extensions' should have 'bar' extension", () => {
        cloudevent.addExtension("bar", "value");
        expect(cloudevent.format().extensions)
          .to.have.property("foo");
      });

    });

    describe("The Constraints check", () => {
      describe("'eventType'", () => {
        it("should throw an error when is an empty string", () => {
          cloudevent.type("");
          expect(cloudevent.format.bind(cloudevent))
            .to
            .throw("'eventType' is invalid");
        });

        it("must be a non-empty string", () => {
          cloudevent.type(type);
          cloudevent.format();
        });

        it("should be prefixed with a reverse-DNS name", () => {
          //TODO how to assert it?
        });
      });

      //TODO another attributes . . .

      describe("'eventTime'", () => {
        it("must adhere to the format specified in RFC 3339", () => {
          cloudevent.time(time);
          expect(cloudevent.format()["eventTime"]).to.equal(time.toISOString());
        });
      });
    });

  });

});
<commit_msg>deduplication<commit_after>var expect       = require("chai").expect;
var Cloudevent   = require("../index.js");

const type   = "com.github.pull.create01";
const source = "urn:event:from:myapi/resourse/01";
const time   = new Date();
const schemaurl = "http://example.com/registry/v01/myschema.json";
const contenttype = "application/json";
const data = {};
const extensions = {};

var cloudevent = new Cloudevent()
                       .type(type)
                       .source(source);

describe("CloudEvents Spec 0.1 - JavaScript SDK", () => {

  describe("JSON Format", () => {

    describe("Required context attributes", () => {
      it("requires 'eventType'", () => {
        expect(cloudevent.format()).to.have.property("eventType");
      });

      it("requires 'eventTypeVersion'", () => {
        cloudevent.eventTypeVersion("1.0");
        expect(cloudevent.format()).to.have.property("eventTypeVersion");
      });

      it("requires 'cloudEventsVersion'", () => {
        expect(cloudevent.format()).to.have.property("cloudEventsVersion");
      });

      it("requires 'source'", () => {
        expect(cloudevent.format()).to.have.property("source");
      });

      it("requires 'eventID'", () => {
        expect(cloudevent.format()).to.have.property("eventID");
      });
    });

    describe("Optional context attributes", () => {
      it("contains 'eventTime'", () => {
        cloudevent.time(time);
        expect(cloudevent.format()).to.have.property("eventTime");
      });

      it("contains 'schemaURL'", () => {
        cloudevent.schemaurl(schemaurl);
        expect(cloudevent.format()).to.have.property("schemaURL");
      });

      it("contains 'contentType'", () => {
        cloudevent.contenttype(contenttype);
        expect(cloudevent.format()).to.have.property("contentType");
      });

      it("contains 'data'", () => {
        cloudevent.data(data);
        expect(cloudevent.format()).to.have.property("data");
      });

      it("contains 'extensions'", () => {
        cloudevent.addExtension("foo", "value");
        expect(cloudevent.format()).to.have.property("extensions");
      });

      it("'extensions' should have 'bar' extension", () => {
        cloudevent.addExtension("bar", "value");
        expect(cloudevent.format().extensions)
          .to.have.property("foo");
      });

    });

    describe("The Constraints check", () => {
      describe("'eventType'", () => {
        it("should throw an error when is an empty string", () => {
          cloudevent.type("");
          expect(cloudevent.format.bind(cloudevent))
            .to
            .throw("'eventType' is invalid");
        });

        it("must be a non-empty string", () => {
          cloudevent.type(type);
          cloudevent.format();
        });

        it("should be prefixed with a reverse-DNS name", () => {
          //TODO how to assert it?
        });
      });

      //TODO another attributes . . .

      describe("'eventTime'", () => {
        it("must adhere to the format specified in RFC 3339", () => {
          cloudevent.time(time);
          expect(cloudevent.format()["eventTime"]).to.equal(time.toISOString());
        });
      });
    });

  });

});
<|endoftext|>


<commit_before>// Go get a hook to BackstopJS
const backstop = require('../../core/runner');

// Run BackstopJS with docker
// NOTE: passing either config file name or actual config object is supported.
backstop('test', {
  docker: true,
  config: 'backstop_alt',
  filter: undefined
}).then(
  () => console.log('nothing new'),
  () => console.log('changes found')
);
<commit_msg>this is what reference looks like<commit_after>// Go get a hook to BackstopJS
const backstop = require('../../core/runner');

// Run BackstopJS with docker
// NOTE: passing either config file name or actual config object is supported.
backstop('reference', {
  docker: false,
  config: 'backstop',
  filter: undefined,
  i: false
}).then(
  () => console.log('nothing new'),
  () => console.log('changes found')
);
<|endoftext|>


<commit_before>describe('React server side rendering', function () {
    it('should be good', function () {
        browser.get('main');
        expect(element.all(by.css('ul li')).count()).toEqual(5);
    });

    it('body title should be main', function () {
        browser.get('main');
        expect(element(by.css('div h1')).getInnerHtml()).toMatch(/Main/);
    });

    it('html title should be Main Page', function () {
        expect(element(by.css('title')).getInnerHtml()).toBe('Main Page');
    });
});

describe('React client side binding', function () {
    it('should handle document.title correctly', function () {
        browser.driver.executeScript('return document.title').then(function (value) {
            expect(value).toBe('Main Page');
        });
    });

    it('should handle button click', function () {
        browser.get('main');
        browser.driver.executeScript('return window.test=1');
        element.all(by.css('ul li a')).get(0).click();
        browser.wait(function () {
            return browser.driver.getCurrentUrl().then(function (url) {
                return url.match(/product\/123/);
            });
        });
        expect(element(by.css('div span')).getInnerHtml()).toMatch(/12300/);
    });

    it('should block page loading and use history.pushState', function () {
        browser.driver.executeScript('return window.test').then(function (value) {
            expect(value).toBe(1);
        });
    });

    it('should change title correctly after page changed', function () {
        browser.driver.executeScript('return document.title').then(function (value) {
            expect(value).toMatch(/123/);
        });
    });

    it('should handle browser back button', function () {
        browser.driver.executeScript('return window.test=2');
        browser.navigate().back();
        browser.wait(function () {
            return browser.driver.getCurrentUrl().then(function (url) {
                return url.match(/main/);
            });
        });
        expect(element(by.css('div h1')).getInnerHtml()).toMatch(/Main/);
    });

    it('should not cause page changed when back', function () {
        browser.driver.executeScript('return window.test').then(function (value) {
            expect(value).toBe(2);
        });
    });

    it('should change title correctly after page back', function () {
        browser.driver.executeScript('return document.title').then(function (value) {
            expect(value).toBe('Main Page');
        });
    });
});<commit_msg>fix for safariDriver testing issue<commit_after>describe('React server side rendering', function () {
    it('should be good', function () {
        browser.get('main');
        expect(element.all(by.css('ul li')).count()).toEqual(5);
    });

    it('body title should be main', function () {
        browser.get('main');
        expect(element(by.css('div h1')).getInnerHtml()).toMatch(/Main/);
    });

    it('html title should be Main Page', function () {
        expect(element(by.css('title')).getInnerHtml()).toBe('Main Page');
    });
});

describe('React client side binding', function () {
    it('should handle document.title correctly', function () {
        browser.driver.executeScript('return document.title').then(function (value) {
            expect(value).toBe('Main Page');
        });
    });

    it('should handle button click', function () {
        browser.get('main');
        browser.driver.executeScript('return window.test=1');
        element.all(by.css('ul li a')).get(0).click();
        browser.wait(function () {
            return browser.driver.getCurrentUrl().then(function (url) {
                return url.match(/product\/123/);
            });
        });
        expect(element(by.css('div span')).getInnerHtml()).toMatch(/12300/);
    });

    it('should block page loading and use history.pushState', function () {
        browser.driver.executeScript('return window.test').then(function (value) {
            expect(value).toBe(1);
        });
    });

    it('should change title correctly after page changed', function () {
        browser.driver.executeScript('return document.title').then(function (value) {
            expect(value).toMatch(/123/);
        });
    });

    it('should handle browser back button', function () {
        // prevent safari issue by javascript history.go(-1)
        // http://stackoverflow.com/questions/27240969/unknown-error-on-safari-history-navigation-using-protractor-webdrivers
        browser.driver.executeScript('return (window.test=2) && (history.go(-1))');

        browser.wait(function () {
            return browser.driver.getCurrentUrl().then(function (url) {
                return url.match(/main/);
            });
        });
        expect(element(by.css('div h1')).getInnerHtml()).toMatch(/Main/);
    });

    it('should not cause page changed when back', function () {
        browser.driver.executeScript('return window.test').then(function (value) {
            expect(value).toBe(2);
        });
    });

    it('should change title correctly after page back', function () {
        browser.driver.executeScript('return document.title').then(function (value) {
            expect(value).toBe('Main Page');
        });
    });
});<|endoftext|>


<commit_before>// Dragging example works in Firefox but has an offset issue with Chrome

const draggables = {
    1: {x: 0, y: 0, action: `alert("Hello from draggable #1")`},
    2: {x: 0, y: 0, action: `alert("Hello from draggable #2")`},
    3: {x: 0, y: 0, action: `alert("Hello from draggable #3")`},
    4: {x: 0, y: 0, name: "Make new item", action: `makeNewItem(context)` },
    5: {x: 0, y: 0, name: "In memory of James R. Beniger", action: `open("https://en.wikipedia.org/wiki/Beniger,_James_R.")` },
    6: {x: 0, y: 0, name: "Log Draggables", action: `console.log("draggables", context.draggables)` },
}

function makeNewItem(context) {
    console.log("makeNewItem", context)
    const name = prompt("new name?")
    if (!name) return
    context.draggables[new Date().toISOString()] = {x: 0, y: 0, name: name }
}

let dragStart

show(() => {
    return m("div.bg-gray.h5.w100", { ondragover: (e) => e.preventDefault(), ondrop: (e) => e.preventDefault() },
        Object.keys(draggables).map((number) => m("div.di.ba.pa2.ma2.relative.bg-green", {
            draggable: true,
            style: {
                cursor: "move",
                top: draggables[number].y + "px",
                left: draggables[number].x + "px",
            },
            ondragstart: (e) => {
                dragStart = {x: e.screenX, y: e.screenY}
                e.dataTransfer.setData('text/plain', number)
                e.dataTransfer.effectAllowed = "move"
            },
            ondragend: (e) => {
                const d = draggables[number]
                const s = dragStart;
                draggables[number].x = d.x + e.screenX - s.x
                draggables[number].y = d.y + e.screenY - s.y
            },
            onclick: draggables[number].action ? function() { const context = { draggables }; eval(draggables[number].action) }: (() => undefined)
        }, draggables[number].name || ("Drag me! " + number)))
      )
    }, ".bg-blue.br4"
)
<commit_msg>Update draggables-extended.js<commit_after>// Dragging example works in Firefox but has an offset issue with Chrome

const draggables = {
    1: {x: 0, y: 0, action: `alert("Hello from draggable #1")`},
    2: {x: 0, y: 50, action: `alert("Hello from draggable #2")`},
    3: {x: 0, y: 100, action: `alert("Hello from draggable #3")`},
    4: {x: 0, y: 150, name: "Make new item", action: makeNewItem },
    5: {x: 0, y: 200, name: "In memory of James R. Beniger", action: `open("https://en.wikipedia.org/wiki/Beniger,_James_R.")` },
    6: {x: 0, y: 250, name: "Log Draggables", action: `console.log("draggables", context.draggables)` },
}

function makeNewItem(context) {
    console.log("makeNewItem", context)
    const name = prompt("new name?")
    if (!name) return
    context.draggables[new Date().toISOString()] = {x: 0, y: 0, name: name }
}

let dragStart

show(() => {
    return m("div.bg-gray.h5.w100.relative", { ondragover: (e) => e.preventDefault(), ondrop: (e) => e.preventDefault() },
        Object.keys(draggables).map((number) => m("div.di.ba.pa2.ma2.bg-green.absolute", {
            draggable: true,
            style: {
                cursor: "move",
                top: draggables[number].y + "px",
                left: draggables[number].x + "px",
            },
            ondragstart: (e) => {
                dragStart = {x: e.screenX, y: e.screenY}
                e.dataTransfer.setData('text/plain', number)
                e.dataTransfer.effectAllowed = "move"
            },
            ondragend: (e) => {
                const d = draggables[number]
                const s = dragStart;
                draggables[number].x = d.x + e.screenX - s.x
                draggables[number].y = d.y + e.screenY - s.y
            },
            onclick: draggables[number].action ? function() {
                const context = { draggables }
                const action = draggables[number].action
                typeof action === "string" ?
                    eval(draggables[number].action) :
                    action(context)
            }: (() => undefined)
        }, draggables[number].name || ("Drag me! " + number)))
      )
    }, ".bg-blue.br4"
)
<|endoftext|>


<commit_before>var Chicago = require('./chicago.js')
var Message = require('./message.js')
var assert = require('assert')
var Duplex = require('readable-stream-no-buffering').Duplex
var inherits = require('inherits')
var Block = require('./message-block.js')
var _ = require('lodash')
var constants = require('./constants.js')
var isBuffer = require('is-buffer')
var winston = require('winston')
var winstonWrapper = require('winston-meta-wrapper')

var MessageStream = function (options) {
  if (!options) {
    options = {}
  }
  if (!options.logger) {
    options.logger = winston
  }
  this._log = winstonWrapper(options.logger)
  this._log.addMeta({
    module: 'curvecp-messagestream'
  })
  var opts = {}
  opts.objectMode = false
  opts.decodeStrings = true
  opts.allowHalfOpen = false
  opts.highWaterMark = 0
  Duplex.call(this, opts)
  this._maxBlockLength = 640 - constants.HEADER_SIZE - constants.MINIMAL_PADDING
  this._stream = options.stream
  this.__streamReady = false
  var self = this
  this._stream.on('data', this._receiveData.bind(this))
  this._stream.on('error', function (error) {
    self.emit('error', error)
  })
  this._stream.on('close', function () {
    self.push(null)
    self._cleanup()
    self._stream.removeAllListeners()
    self.emit('close')
  })
  this._stream.on('end', function () {
    self.push(null)
  })
  this._stream.on('connect', function () {
    self.__streamReady = true
    self.emit('connect')
  })
  this._stream.on('lookup', function (err, address, family) {
    self.emit('lookup', err, address, family)
  })
  this._stream.on('timeout', function () {
    self.emit('timeout')
  })
  if (this._stream.is_server) {
    this._maxBlockLength = constants.MESSAGE_BODY
  }
  /* Bytes that still need to be processed */
  this._sendBytes = new Buffer(0)
  this._stopSuccess = false
  this._stopFailure = false
  this._writeRequests = []
  /* Bytes that have been processed / send to peer */
  this._sendProcessed = 0
  /* Blocks that have been send but not yet acknowledged by other party */
  this._outgoing = []
  /* Messages that have been received but not yet processed */
  this._incoming = []
  /* Number of bytes that have been received and send upstream */
  this._receivedBytes = 0
  /* Chicago congestion control algorithm */
  this._chicago = new Chicago()
  /* nanosecond precision timer */
  this._chicago.set_timeout(this._process.bind(this))
  this.__nextMessageId = 1
}

inherits(MessageStream, Duplex)

MessageStream.prototype._cleanup = function () {
  this._sendBytes = new Buffer(0)
  _.forEach(this._writeRequests, function (request) {
    request.callback(new Error('Underlying stream does not respond anymore'))
  }, this)
  this._writeRequests = []
  this._outgoing = []
}

MessageStream.prototype._nextMessageId = function () {
  var result = this.__nextMessageId
  this.__nextMessageId += 1
  return result
}

MessageStream.prototype._receiveData = function (data) {
  this._log.debug('_receiveData')
  if (_.size(this._incoming) < constants.MAX_INCOMING) {
    var message = new Message()
    message.fromBuffer(data)
    this._incoming.push(message)
  }
  var self = this
  setImmediate(function () {
    if (self.canProcessMessage()) {
      self.processMessage()
    }
  })
}

MessageStream.prototype.connect = function (boxId, connectionInfo) {
  this._stream.connect(boxId, connectionInfo)
}

MessageStream.prototype.isConnected = function () {
  return this._stream.isConnected()
}

MessageStream.prototype.destroy = function () {
  this._stream.destroy()
}

MessageStream.prototype._read = function (size) {}

MessageStream.prototype._process = function () {
  this._log.debug('_process')
  var maxReached = _.some(this._outgoing, function (block) {
    return block.transmissions > constants.MAX_RETRANSMISSIONS
  })
  if (maxReached) {
    this._log.warn('maximum retransmissions reached')
    this._cleanup()
    this.emit('error', new Error('Maximum retransmissions reached - remote host down'))
  }
  if (this.canResend()) {
    this.resendBlock()
  } else if (this.canSend()) {
    this.sendBlock()
  }
  if (_.isEmpty(this._incoming) && _.isEmpty(this._outgoing) && this._sendBytes.length === 0) {
    this._chicago.disable_timer()
  }
}

MessageStream.prototype._write = function (chunk, encoding, done) {
  this._log.debug('_write')
  assert(isBuffer(chunk))
  if (this._sendBytes.length > constants.MAXIMUM_UNPROCESSED_SEND_BYTES) {
    this._log.warn('Buffer is full')
    done(new Error('Buffer is full'))
    return
  }
  this._writeRequests.push({
    startByte: this._sendProcessed,
    length: chunk.length,
    callback: done
  })
  this._sendBytes = Buffer.concat([this._sendBytes, chunk])
  this._log.debug('_sendBytes length: ' + this._sendBytes.length)
  this._log.debug('_sendProcessed length: ' + this._sendProcessed)
  this._chicago.enable_timer()
  return this._sendBytes.length < constants.MAXIMUM_UNPROCESSED_SEND_BYTES
}

MessageStream.prototype._end = function () {
  this._stopSuccess = true
  this._writableState.ended = true
}

MessageStream.prototype.canResend = function () {
  var self = this
  if (this.__streamReady && !_.isEmpty(this._outgoing)) {
    var some = _.some(this._outgoing, function (block) {
      return self._chicago.block_is_timed_out(block.transmission_time)
    })
    return some
  }
  return false
}

MessageStream.prototype.resendBlock = function () {
  this._log.debug('resendBlock')
  var block = this._outgoing[0]
  _.forEach(this._outgoing, function (compareBlock) {
    if (block.transmission_time.compare(compareBlock) > 0) {
      block = compareBlock
    }
  })
  block.transmission_time = this._chicago.get_clock()
  block.transmissions = block.transmissions + 1
  block.id = this._nextMessageId()
  this._chicago.retransmission()
  this._sendBlock(block)
}

MessageStream.prototype.canSend = function () {
  return this.__streamReady && this._sendBytes.length > 0 && _.size(this._outgoing) < constants.MAX_OUTGOING
}

MessageStream.prototype.sendBlock = function () {
  this._log.debug('sendBlock')
  this._log.debug('sendBytes start: ' + this._sendBytes.length)
  this._log.debug('sendProcessed start: ' + this._sendProcessed)
  var blockSize = this._sendBytes.length
  if (blockSize > this._maxBlockLength) {
    blockSize = this._maxBlockLength
  }
  var block = new Block()
  block.start_byte = this._sendProcessed
  block.transmission_time = this._chicago.get_clock()
  block.id = this._nextMessageId()
  block.data = this._sendBytes.slice(0, blockSize)
  if (this._sendBytes.length === blockSize && (this._stopSuccess || this._stopFailure)) {
    block.stop_success = this._stopSuccess
    block.stop_failure = this._stopFailure
  }
  this._sendBytes = this._sendBytes.slice(blockSize)
  this._sendProcessed = this._sendProcessed + block.data.length
  this._log.debug('sendBytes stop: ' + this._sendBytes.length)
  this._log.debug('sendProcessed stop: ' + this._sendProcessed)
  this._outgoing.push(block)
  this._sendBlock(block)
  if (this._sendBytes.length + block.data.length > constants.MAXIMUM_UNPROCESSED_SEND_BYTES * 0.5 &&
    this._sendBytes.length < constants.MAXIMUM_UNPROCESSED_SEND_BYTES * 0.5) {
    this.emit('drain')
  }
}

MessageStream.prototype._sendBlock = function (block) {
  this._log.debug('_sendBlock ' + block.start_byte + ' - ' + block.data.length)
  var message = new Message()
  message.id = block.id
  message.acknowledging_range_1_size = this._receivedBytes
  message.data = block.data
  message.success = block.stop_success
  message.failure = block.stop_failure
  message.offset = block.start_byte
  this._chicago.send_block()
  this._writeToStream(message)
  this._maxBlockLength = constants.MESSAGE_BODY
}

MessageStream.prototype.canProcessMessage = function () {
  return this._incoming.length > 0
}

MessageStream.prototype.processMessage = function () {
  this._log.debug('processMessage')
  var message = this._incoming.shift()
  this.processAcknowledgments(message)
  this._processMessage(message)
}

MessageStream.prototype.processAcknowledgments = function (message) {
  var self = this
  this._log.debug('processAcknowledgements')
  var removedList
  removedList = _.remove(this._outgoing, function (block) {
    return message.isAcknowledged(block.start_byte, block.data.length)
  }, this)
  _.forEach(removedList, function (block) {
    this._log.debug('block acknowledged: ' + block.start_byte + ' - ' + block.data.length)
    this._chicago.acknowledgement(block.transmission_time)
  }, this)
  removedList = _.remove(this._writeRequests, function (writeRequest) {
    return message.isAcknowledged(writeRequest.startByte, writeRequest.length)
  }, this)
  _.forEach(removedList, function (writeRequest) {
    self._log.debug('write request acknowledged: ' + writeRequest.startByte + ' - ' + writeRequest.length)
    writeRequest.callback()
  })
  if ((this._stopSuccess || this._stopFailure) && this._sendBytes.length === 0 && this._outgoing.length === 0) {
    this.emit('finish')
  }
}

MessageStream.prototype.sendAcknowledgment = function (message) {
  this._log.debug('sendAcknowledgment ' + this._receivedBytes)
  var reply = new Message()
  reply.id = this._nextMessageId()
  reply.acknowledging_id = message.id
  reply.acknowledging_range_1_size = this._receivedBytes
  this._writeToStream(reply)
}

MessageStream.prototype._processMessage = function (message) {
  this._log.debug('_processMessage')
  if (message.offset <= this._receivedBytes) {
    if (message.data_length > 1) {
      var ignoreBytes = this._receivedBytes - message.offset
      var data = message.data.slice(ignoreBytes)
      this._receivedBytes += data.length
      // debug(data.toString())
      this.push(data)
      if (message.success || message.failure) {
        this.push(null)
      }
      this.sendAcknowledgment(message)
    }
  }
}

MessageStream.prototype._writeToStream = function (message) {
  this.__streamReady = false
  this._stream.write(message.toBuffer(), this._processReady.bind(this))
}

MessageStream.prototype._processReady = function (err) {
  if (!err) {
    this.__streamReady = true
  } else {
    this._log.warn('error while sending CurveCP message')
  }
}

Object.defineProperty(MessageStream.prototype, 'remoteAddress', {
  get: function () {
    return this._stream.remoteAddress
  }
})

module.exports = MessageStream
<commit_msg>switch from setImmediate to nextTick<commit_after>var Chicago = require('./chicago.js')
var Message = require('./message.js')
var assert = require('assert')
var Duplex = require('readable-stream-no-buffering').Duplex
var inherits = require('inherits')
var Block = require('./message-block.js')
var _ = require('lodash')
var constants = require('./constants.js')
var isBuffer = require('is-buffer')
var winston = require('winston')
var winstonWrapper = require('winston-meta-wrapper')

var MessageStream = function (options) {
  if (!options) {
    options = {}
  }
  if (!options.logger) {
    options.logger = winston
  }
  this._log = winstonWrapper(options.logger)
  this._log.addMeta({
    module: 'curvecp-messagestream'
  })
  var opts = {}
  opts.objectMode = false
  opts.decodeStrings = true
  opts.allowHalfOpen = false
  opts.highWaterMark = 0
  Duplex.call(this, opts)
  this._maxBlockLength = 640 - constants.HEADER_SIZE - constants.MINIMAL_PADDING
  this._stream = options.stream
  this.__streamReady = false
  var self = this
  this._stream.on('data', this._receiveData.bind(this))
  this._stream.on('error', function (error) {
    self.emit('error', error)
  })
  this._stream.on('close', function () {
    self.push(null)
    self._cleanup()
    self._stream.removeAllListeners()
    self.emit('close')
  })
  this._stream.on('end', function () {
    self.push(null)
  })
  this._stream.on('connect', function () {
    self.__streamReady = true
    self.emit('connect')
  })
  this._stream.on('lookup', function (err, address, family) {
    self.emit('lookup', err, address, family)
  })
  this._stream.on('timeout', function () {
    self.emit('timeout')
  })
  if (this._stream.is_server) {
    this._maxBlockLength = constants.MESSAGE_BODY
  }
  /* Bytes that still need to be processed */
  this._sendBytes = new Buffer(0)
  this._stopSuccess = false
  this._stopFailure = false
  this._writeRequests = []
  /* Bytes that have been processed / send to peer */
  this._sendProcessed = 0
  /* Blocks that have been send but not yet acknowledged by other party */
  this._outgoing = []
  /* Messages that have been received but not yet processed */
  this._incoming = []
  /* Number of bytes that have been received and send upstream */
  this._receivedBytes = 0
  /* Chicago congestion control algorithm */
  this._chicago = new Chicago()
  /* nanosecond precision timer */
  this._chicago.set_timeout(this._process.bind(this))
  this.__nextMessageId = 1
}

inherits(MessageStream, Duplex)

MessageStream.prototype._cleanup = function () {
  this._sendBytes = new Buffer(0)
  _.forEach(this._writeRequests, function (request) {
    request.callback(new Error('Underlying stream does not respond anymore'))
  }, this)
  this._writeRequests = []
  this._outgoing = []
}

MessageStream.prototype._nextMessageId = function () {
  var result = this.__nextMessageId
  this.__nextMessageId += 1
  return result
}

MessageStream.prototype._receiveData = function (data) {
  this._log.debug('_receiveData')
  if (_.size(this._incoming) < constants.MAX_INCOMING) {
    var message = new Message()
    message.fromBuffer(data)
    this._incoming.push(message)
  }
  var self = this
  process.nextTick(function () {
    if (self.canProcessMessage()) {
      self.processMessage()
    }
  })
}

MessageStream.prototype.connect = function (boxId, connectionInfo) {
  this._stream.connect(boxId, connectionInfo)
}

MessageStream.prototype.isConnected = function () {
  return this._stream.isConnected()
}

MessageStream.prototype.destroy = function () {
  this._stream.destroy()
}

MessageStream.prototype._read = function (size) {}

MessageStream.prototype._process = function () {
  this._log.debug('_process')
  var maxReached = _.some(this._outgoing, function (block) {
    return block.transmissions > constants.MAX_RETRANSMISSIONS
  })
  if (maxReached) {
    this._log.warn('maximum retransmissions reached')
    this._cleanup()
    this.emit('error', new Error('Maximum retransmissions reached - remote host down'))
  }
  if (this.canResend()) {
    this.resendBlock()
  } else if (this.canSend()) {
    this.sendBlock()
  }
  if (_.isEmpty(this._incoming) && _.isEmpty(this._outgoing) && this._sendBytes.length === 0) {
    this._chicago.disable_timer()
  }
}

MessageStream.prototype._write = function (chunk, encoding, done) {
  this._log.debug('_write')
  assert(isBuffer(chunk))
  if (this._sendBytes.length > constants.MAXIMUM_UNPROCESSED_SEND_BYTES) {
    this._log.warn('Buffer is full')
    done(new Error('Buffer is full'))
    return
  }
  this._writeRequests.push({
    startByte: this._sendProcessed,
    length: chunk.length,
    callback: done
  })
  this._sendBytes = Buffer.concat([this._sendBytes, chunk])
  this._log.debug('_sendBytes length: ' + this._sendBytes.length)
  this._log.debug('_sendProcessed length: ' + this._sendProcessed)
  this._chicago.enable_timer()
  return this._sendBytes.length < constants.MAXIMUM_UNPROCESSED_SEND_BYTES
}

MessageStream.prototype._end = function () {
  this._stopSuccess = true
  this._writableState.ended = true
}

MessageStream.prototype.canResend = function () {
  var self = this
  if (this.__streamReady && !_.isEmpty(this._outgoing)) {
    var some = _.some(this._outgoing, function (block) {
      return self._chicago.block_is_timed_out(block.transmission_time)
    })
    return some
  }
  return false
}

MessageStream.prototype.resendBlock = function () {
  this._log.debug('resendBlock')
  var block = this._outgoing[0]
  _.forEach(this._outgoing, function (compareBlock) {
    if (block.transmission_time.compare(compareBlock) > 0) {
      block = compareBlock
    }
  })
  block.transmission_time = this._chicago.get_clock()
  block.transmissions = block.transmissions + 1
  block.id = this._nextMessageId()
  this._chicago.retransmission()
  this._sendBlock(block)
}

MessageStream.prototype.canSend = function () {
  return this.__streamReady && this._sendBytes.length > 0 && _.size(this._outgoing) < constants.MAX_OUTGOING
}

MessageStream.prototype.sendBlock = function () {
  this._log.debug('sendBlock')
  this._log.debug('sendBytes start: ' + this._sendBytes.length)
  this._log.debug('sendProcessed start: ' + this._sendProcessed)
  var blockSize = this._sendBytes.length
  if (blockSize > this._maxBlockLength) {
    blockSize = this._maxBlockLength
  }
  var block = new Block()
  block.start_byte = this._sendProcessed
  block.transmission_time = this._chicago.get_clock()
  block.id = this._nextMessageId()
  block.data = this._sendBytes.slice(0, blockSize)
  if (this._sendBytes.length === blockSize && (this._stopSuccess || this._stopFailure)) {
    block.stop_success = this._stopSuccess
    block.stop_failure = this._stopFailure
  }
  this._sendBytes = this._sendBytes.slice(blockSize)
  this._sendProcessed = this._sendProcessed + block.data.length
  this._log.debug('sendBytes stop: ' + this._sendBytes.length)
  this._log.debug('sendProcessed stop: ' + this._sendProcessed)
  this._outgoing.push(block)
  this._sendBlock(block)
  if (this._sendBytes.length + block.data.length > constants.MAXIMUM_UNPROCESSED_SEND_BYTES * 0.5 &&
    this._sendBytes.length < constants.MAXIMUM_UNPROCESSED_SEND_BYTES * 0.5) {
    this.emit('drain')
  }
}

MessageStream.prototype._sendBlock = function (block) {
  this._log.debug('_sendBlock ' + block.start_byte + ' - ' + block.data.length)
  var message = new Message()
  message.id = block.id
  message.acknowledging_range_1_size = this._receivedBytes
  message.data = block.data
  message.success = block.stop_success
  message.failure = block.stop_failure
  message.offset = block.start_byte
  this._chicago.send_block()
  this._writeToStream(message)
  this._maxBlockLength = constants.MESSAGE_BODY
}

MessageStream.prototype.canProcessMessage = function () {
  return this._incoming.length > 0
}

MessageStream.prototype.processMessage = function () {
  this._log.debug('processMessage')
  var message = this._incoming.shift()
  this.processAcknowledgments(message)
  this._processMessage(message)
}

MessageStream.prototype.processAcknowledgments = function (message) {
  var self = this
  this._log.debug('processAcknowledgements')
  var removedList
  removedList = _.remove(this._outgoing, function (block) {
    return message.isAcknowledged(block.start_byte, block.data.length)
  }, this)
  _.forEach(removedList, function (block) {
    this._log.debug('block acknowledged: ' + block.start_byte + ' - ' + block.data.length)
    this._chicago.acknowledgement(block.transmission_time)
  }, this)
  removedList = _.remove(this._writeRequests, function (writeRequest) {
    return message.isAcknowledged(writeRequest.startByte, writeRequest.length)
  }, this)
  _.forEach(removedList, function (writeRequest) {
    self._log.debug('write request acknowledged: ' + writeRequest.startByte + ' - ' + writeRequest.length)
    writeRequest.callback()
  })
  if ((this._stopSuccess || this._stopFailure) && this._sendBytes.length === 0 && this._outgoing.length === 0) {
    this.emit('finish')
  }
}

MessageStream.prototype.sendAcknowledgment = function (message) {
  this._log.debug('sendAcknowledgment ' + this._receivedBytes)
  var reply = new Message()
  reply.id = this._nextMessageId()
  reply.acknowledging_id = message.id
  reply.acknowledging_range_1_size = this._receivedBytes
  this._writeToStream(reply)
}

MessageStream.prototype._processMessage = function (message) {
  this._log.debug('_processMessage')
  if (message.offset <= this._receivedBytes) {
    if (message.data_length > 1) {
      var ignoreBytes = this._receivedBytes - message.offset
      var data = message.data.slice(ignoreBytes)
      this._receivedBytes += data.length
      // debug(data.toString())
      this.push(data)
      if (message.success || message.failure) {
        this.push(null)
      }
      this.sendAcknowledgment(message)
    }
  }
}

MessageStream.prototype._writeToStream = function (message) {
  this.__streamReady = false
  this._stream.write(message.toBuffer(), this._processReady.bind(this))
}

MessageStream.prototype._processReady = function (err) {
  if (!err) {
    this.__streamReady = true
  } else {
    this._log.warn('error while sending CurveCP message')
  }
}

Object.defineProperty(MessageStream.prototype, 'remoteAddress', {
  get: function () {
    return this._stream.remoteAddress
  }
})

module.exports = MessageStream
<|endoftext|>


<commit_before>import { dirname, extname } from 'path'
import fs from 'graceful-fs'

import log from 'npmlog'
import { promisify } from 'bluebird'
import Bluebird from 'bluebird'

import scanFLAC from '../flac/scan.js'
import { unpack as unzip } from '../utils/zip.js'
import { Cover, File } from '@packard/model'

const stat = promisify(fs.stat)

function scan (unpackedFiles, trackers) {
  return Bluebird.map(
    unpackedFiles,
    ({ path, sourceArchive, flacTrack }) => {
      switch (extname(path)) {
        case '.flac':
          if (flacTrack) {
            if (sourceArchive) flacTrack.sourceArchive = sourceArchive
            return Bluebird.resolve(flacTrack)
          } else {
            return scanFLAC(path, trackers, { sourceArchive })
          }
          break
        case '.jpg':
        case '.pdf':
        case '.png':
          return stat(path).then(stats => new Cover(path, stats))
        case '.avi':
          return stat(path).then(stats => new File(path, stats))
        default:
          log.warn('scan', "don't recognize type of", path)
          return stat(path).then(stats => new File(path, stats))
      }
    }
  )
}

function populateImages (list, covers) {
  list.filter(e => e instanceof Cover)
      .forEach(c => {
        const directory = dirname(c.path)
        if (!covers.get(directory)) {
          log.silly('scan', 'creating image list for', directory)
          covers.set(directory, [])
        }
        log.silly('scan', 'cover', c)
        covers.get(directory).push(c)
      })

  return list
}

export function extractRelease (zipfile, tmpdir, covers, trackers) {
  log.verbose('extractReleaseMetadata', 'archive:', zipfile)

  return unzip(zipfile, trackers, tmpdir)
          .then(list => scan(list, trackers))
          .then(list => populateImages(list, covers))
          .then(list => list.filter(e => !(e instanceof Cover ||
                                           e instanceof Error)))
}
<commit_msg>metadata: videos are just files for now<commit_after>import { dirname, extname } from 'path'
import fs from 'graceful-fs'

import log from 'npmlog'
import Bluebird from 'bluebird'

import scanFLAC from '../flac/scan.js'
import { unpack as unzip } from '../utils/zip.js'
import { Cover, File } from '@packard/model'

import { promisify } from 'bluebird'
const stat = promisify(fs.stat)

function scan (unpackedFiles, trackers) {
  return Bluebird.map(
    unpackedFiles,
    ({ path, sourceArchive, flacTrack }) => {
      switch (extname(path)) {
        case '.flac':
          if (flacTrack) {
            if (sourceArchive) flacTrack.sourceArchive = sourceArchive
            return Bluebird.resolve(flacTrack)
          } else {
            return scanFLAC(path, trackers, { sourceArchive })
          }
          break
        case '.jpg':
        case '.pdf':
        case '.png':
          return stat(path).then(stats => new Cover(path, stats))
        default:
          log.warn('scan', "don't recognize type of", path)
          return stat(path).then(stats => new File(path, stats))
      }
    }
  )
}

function populateImages (list, covers) {
  list.filter(e => e instanceof Cover)
      .forEach(c => {
        const directory = dirname(c.path)
        if (!covers.get(directory)) {
          log.silly('scan', 'creating image list for', directory)
          covers.set(directory, [])
        }
        log.silly('scan', 'cover', c)
        covers.get(directory).push(c)
      })

  return list
}

export function extractRelease (zipfile, tmpdir, covers, trackers) {
  log.verbose('extractReleaseMetadata', 'archive:', zipfile)

  return unzip(zipfile, trackers, tmpdir)
          .then(list => scan(list, trackers))
          .then(list => populateImages(list, covers))
          .then(list => list.filter(e => !(e instanceof Cover ||
                                           e instanceof Error)))
}
<|endoftext|>


<commit_before>/* file: protractor-conf.js */
/*!
 * Copyright 2017 ikarus512
 * https://github.com/ikarus512/fcc1.git
 *
 * DESCRIPTION:
 * AUTHOR: ikarus512
 * CREATED: 2017/03/13
 *
 * MODIFICATION HISTORY
 *  2017/04/04, ikarus512. Added copyright header.
 *
 */

// jscs:disable maximumLineLength

exports.config = {
    // browser.pause(); //https://github.com/angular/protractor/blob/master/docs/debugging.md

    //seleniumAddress: 'http://localhost:4444/wd/hub',
    //localSeleniumStandaloneOpts: {args: '--standalone'},
    specs: ['routes/**/*.js'],

    // baseUrl: 'https://localhost:5000/',
    seleniumServerJar: './../../node_modules/protractor/node_modules/webdriver-manager/selenium/selenium-server-standalone-2.53.1.jar',
    seleniumPort: 4444,
    seleniumArgs: '',
    directConnect: true,

    // getPageTimeout: 10000,
    // allScriptsTimeout: 10000,
    rootElement: '.ng-app', // element where ng-app directive used

    framework: 'mocha',
    mochaOpts: {
        reporter: 'dot',
        // defaultTimeoutInterval: 10000,
        // enableTimeouts: false,
        timeout: 20000,//10000,
        // slow: 5000
    },
    // framework: 'jasmine',
    // jasmineNodeOpts: {
    //   showColors: false,
    //   defaultTimeoutInterval: 10000,
    // },

    // onPrepare: '../test-utils.js',

    multiCapabilities: [{
        browserName: 'chrome', // needs to run:   webdriver-manager update
        // shardTestFiles: true, //each spec file in different browser instance
        // maxInstances: 2       //number of instances
        // }, {
        //   browserName: 'firefox'
        // }, {
        // browserName: 'internet explorer', // needs to run:   webdriver-manager update --ie
        // acceptSslCerts: true,
        // trustAllSSLCertificates: true,
        // platform: 'ANY',
        // version: '11'
    }],
    // maxSessions: 4,

    // localSeleniumStandaloneOpts : {
    //   jvmArgs : ['-Dwebdriver.ie.driver=node_modules/protractor/node_modules/webdriver-manager/selenium/IEDriverServer.exe']
    // },
};

// // Start server for testing on port 9201.
// var server = require('child_process').spawn('node', ['server.js', '--port=9201']);
// process.on('exit', function() {
//     server.kill('SIGTERM');
// });
<commit_msg>Travis CI e2e tests fix<commit_after>/* file: protractor-conf.js */
/*!
 * Copyright 2017 ikarus512
 * https://github.com/ikarus512/fcc1.git
 *
 * DESCRIPTION:
 * AUTHOR: ikarus512
 * CREATED: 2017/03/13
 *
 * MODIFICATION HISTORY
 *  2017/04/04, ikarus512. Added copyright header.
 *
 */

// jscs:disable maximumLineLength

exports.config = {
    // browser.pause(); //https://github.com/angular/protractor/blob/master/docs/debugging.md

    //seleniumAddress: 'http://localhost:4444/wd/hub',
    //localSeleniumStandaloneOpts: {args: '--standalone'},
    specs: ['routes/**/*.js'],

    // baseUrl: 'https://localhost:5000/',
    seleniumServerJar: './../../node_modules/protractor/node_modules/webdriver-manager/selenium/selenium-server-standalone-2.53.1.jar',
    seleniumPort: 4444,
    seleniumArgs: '',
    directConnect: true,

    // getPageTimeout: 10000,
    // allScriptsTimeout: 10000,
    rootElement: '.ng-app', // element where ng-app directive used

    framework: 'mocha',
    mochaOpts: {
        reporter: 'dot',
        // defaultTimeoutInterval: 10000,
        // enableTimeouts: false,
        timeout: 40000,//10000,
        // slow: 5000
    },
    // framework: 'jasmine',
    // jasmineNodeOpts: {
    //   showColors: false,
    //   defaultTimeoutInterval: 10000,
    // },

    // onPrepare: '../test-utils.js',

    multiCapabilities: [{
        browserName: 'chrome', // needs to run:   webdriver-manager update
        // shardTestFiles: true, //each spec file in different browser instance
        // maxInstances: 2       //number of instances
        // }, {
        //   browserName: 'firefox'
        // }, {
        // browserName: 'internet explorer', // needs to run:   webdriver-manager update --ie
        // acceptSslCerts: true,
        // trustAllSSLCertificates: true,
        // platform: 'ANY',
        // version: '11'
    }],
    // maxSessions: 4,

    // localSeleniumStandaloneOpts : {
    //   jvmArgs : ['-Dwebdriver.ie.driver=node_modules/protractor/node_modules/webdriver-manager/selenium/IEDriverServer.exe']
    // },
};

// // Start server for testing on port 9201.
// var server = require('child_process').spawn('node', ['server.js', '--port=9201']);
// process.on('exit', function() {
//     server.kill('SIGTERM');
// });
<|endoftext|>


<commit_before>'use strict';

process.stdout.write('string literals: ');
console.dir({
  str0: ''',
  str1: """,
});

process.stdout.write('RegExp literals: ');
console.dir({
  regexp0: /I'm the easiest in Chomsky hierarchy!/,
});
<commit_msg>tests: not a comment: snake-y ASCII art<commit_after>'use strict';

process.stdout.write('string literals: ');
console.dir({
  str0: ''',
  str1: """,
  str2: ". // ' \\ . // ' \\ .",
});

process.stdout.write('RegExp literals: ');
console.dir({
  regexp0: /I'm the easiest in Chomsky hierarchy!/,
});
<|endoftext|>