<commit_before>/*
 * Copyright 2014 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Note: There is a single instance of an event bus shared by all messengers

/**
 * Creates the event bus to be used for communication between widgets. If running outside of OWF,
 * If in OWF, a {{#crossLink "neon.eventing.EventBus"}}{{/crossLink}} is returned. If running inside of OWF, a
 * {{#crossLink "neon.eventing.owf.OWFEventBus"}}{{/crossLink}} is returned.
 * @return {neon.eventing.EventBus | neon.eventing.owf.OWFEventBus}
 * @private
 * @method createEventBus_
 */
neon.eventing.createEventBus_ = function () {
	if (neon.util.owfUtils.isRunningInOWF()) {
		return new neon.eventing.owf.OWFEventBus();
	}
	return new neon.eventing.EventBus();
};

/**
 * The event bus used for communication between widgets
 * @property eventBus_
 * @type {neon.eventing.EventBus|neon.eventing.owf.OWFEventBus}
 * @private
 */
neon.eventing.eventBus_ = neon.eventing.createEventBus_();

/**
 * A messenger is a widget's gateway to the event bus used for communication between widgets. Widgets publish and
 * subscribe to messages through a messenger. Each messenger has a unique id to identify which widget published the
 * message to ensure that widgets are not receiving their own messages to facilitate simpler widget code.
 * @class neon.eventing.Messenger
 * @constructor
 */

neon.eventing.Messenger = function () {
	this.id_ = uuid.v4();
};

/**
 * Publishes a message to a channel
 * @param {String} channel The channel to publish the message to
 * @param {Object} message The message to publish
 * @method publish
 */
neon.eventing.Messenger.prototype.publish = function (channel, message) {
	var data = { payload: message, sender: this.id_ };
	neon.eventing.eventBus_.publish(channel, data);
};

/**
 * Subscribes to the channel to receives any messages published to it. Note that messengers will not receive their
 * own messages even if they are subscribed to that channel.
 * @param {String} channel The channel to subscribe to
 * @param {Function} callback The callback to invoke when a message is received on the channel. The function takes
 * 2 parameters: the full data object that was published, and the message that was published as the nested 'payload' object.
 * @method subscribe
 */
neon.eventing.Messenger.prototype.subscribe = function (channel, callback) {
	var me = this;
	neon.eventing.eventBus_.subscribe(channel, function (message) {
		if (message.sender !== me.id_) {
			callback(message, message.payload);
		}
	});
};

/**
 * Unsubscribes this messenger from the channel
 * @param {String} channel The channel to unsubscribe from
 * @method unsubscribe
 */
neon.eventing.Messenger.prototype.unsubscribe = function (channel) {
	neon.eventing.eventBus_.unsubscribe(channel);
};

/**
 * Subscribe to events notifications
 * @param callbacks {object} An object containing callback functions. Each function takes one
 * parameter, the message that was published.
 *

 *
selectionChanged - function to execute when the selection has changed

 *
filtersChanged - function to execute when the filters have been changed

 *
activeDatasetChanged - function to execute when the active dataset has changed

 *

 * @method events
 */
neon.eventing.Messenger.prototype.events = function (callbacks) {
	var me = this;
	var globalChannelConfigs = this.createGlobalChannelSubscriptions_(callbacks);
	_.each(globalChannelConfigs, function (channelConfig) {
		me.subscribe(channelConfig.channel, function (message, payload) {
				if (channelConfig.callback && typeof channelConfig.callback === 'function') {
					channelConfig.callback(message, payload);
				}
			}
		);
	});
};

neon.eventing.Messenger.prototype.createGlobalChannelSubscriptions_ = function (neonCallbacks) {
	// some of the callbacks may be null/undefined, which is ok since they will be ignored
	return [
		{channel: neon.eventing.channels.SELECTION_CHANGED, callback: neonCallbacks.selectionChanged},
		{channel: neon.eventing.channels.FILTERS_CHANGED, callback: neonCallbacks.filtersChanged},
		{channel: neon.eventing.channels.ACTIVE_DATASET_CHANGED, callback: neonCallbacks.activeDatasetChanged}
	];
};

/**
 * Adds a filter to the data being viewed, so only data matching that filter is returned from queries. This will
 * fire a filter changed event to notify other widgets that the filters have changed. If a filter with this id
 * already exists, the filter is appended to that one with an AND clause. To replace the filter entirely, use
 * {{#crossLink "neon.eventing.Messenger/replaceFilter:method"}}{{/crossLink}}.
 * @param {String} id The id of the filter to apply. A typical value to use for the id is the return value of
 * {{#crossLink "neon.widget/getInstanceId:method"}}{{/crossLink}}
 * @param {neon.query.Filter} filter The filter to add
 * @param {Function} successCallback The callback to invoke when the filter is added
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method addFilter
 */
neon.eventing.Messenger.prototype.addFilter = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('filterservice', 'addfilter'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback
		}
	);
};

/**
 * Removes a previously added filter. This will fire a filter changed event to notify other widgets that the filters
 * have changed.
 * @param {String} id The id of the filter to remove. This will be the same id used to create the filter.
 * @param {Function} successCallback The callback to invoke when the filter is removed
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method removeFilter
 */
neon.eventing.Messenger.prototype.removeFilter = function (id, successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('filterservice', 'removefilter'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback,
			data: id,
			contentType: 'text/plain',
			responseType: 'json'
		}
	);
};

/**
 * Replaces a previously added filter. This is a similar to calling remove and then add, but does so with one
 * notification event.
 * @param {String} id The id of the filter to replace.
 * @param {neon.query.Filter} filter The filter to replace the old one with
 * @param {Function} successCallback The callback to invoke when the filter is replaced
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method replaceFilter
 */
neon.eventing.Messenger.prototype.replaceFilter = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('filterservice', 'replacefilter'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback
		}
	);
};

/**
 * Clears all filters. This will fire a filter changed event to notify other widgets that the filters have changed.
 * @method clearFilters
 */
neon.eventing.Messenger.prototype.clearFilters = function (successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('filterservice', 'clearfilters'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback,
			responseType: 'json'
		}
	);
};

/**
 * Clears all filters but does not notify other widgets that the filters have been changed. This is useful if this is
 * part of another event that may result in the widgets updating themselves
 * @method clearFiltersSilently
 */
neon.eventing.Messenger.prototype.clearFiltersSilently = function (successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('filterservice', 'clearfilters'),
		{
			success: successCallback,
			error: errorCallback,
			responseType: 'json'
		}
	);
};

/**
 * Adds any elements matching the filter to the current selection. This will fire a selection changed event to notify
 * other widgets that the selection has changed.
 * @param {String} id The id of the selection to apply. A typical value to use for the id is the return value of
 * {{#crossLink "neon.widget/getInstanceId:method"}}{{/crossLink}}
 * @param {neon.query.Filter} filter Items matching this filter will be marked as selected
 * @param {Function} successCallback The callback to invoke when the selection is added
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method addSelection
 */
neon.eventing.Messenger.prototype.addSelection = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('selectionservice', 'addselection'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false
		}
	);
};

/**
 * Removes this filter from the selected items. This will fire a selection changed event to notify
 * other widgets that the selection has changed.
 * @param {String} id The id of the selection to remove. This will be the same id used to create the selection.
 * @param {Function} successCallback The callback to invoke when the selection is removed
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method removeSelection
 */
neon.eventing.Messenger.prototype.removeSelection = function (id, successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('selectionservice', 'removeselection'),
		{
			success:  this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false,
			data: id,
			contentType: 'text/plain',
			responseType: 'json'
		}
	);
};

/**
 * Replaces a previously added selection. This is a similar to calling remove and then add, but does so with one
 * notification event.
 * @param {String} id The id of the selection to replace.
 * @param {neon.query.Filter} filter The filter that defines the new selection
 * @param {Function} successCallback The callback to invoke when the selection is replaced
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method replaceFilter
 */

neon.eventing.Messenger.prototype.replaceSelection = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('selectionservice', 'replaceselection'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false
		}
	);
};

/**
 * Clears the currently selected items. This will fire a selection changed event to notify
 * other widgets that the selection has changed.
 * @method clearSelection
 */
neon.eventing.Messenger.prototype.clearSelection = function (successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('selectionservice', 'clearselection'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false,
			responseType: 'json'
		}
	);
};

neon.eventing.Messenger.prototype.createFilterKey_ = function (id, filter) {
	return {
		id : id,
		filter: filter
	};
};

neon.eventing.Messenger.prototype.createChannelCallback_ = function (channelName, successCallback) {
	var me = this;
	var callback = function (results) {
		// pass the results in the callback and publish them to the channel so callers can listen
		// in either place
		if (successCallback && typeof successCallback === 'function') {
			successCallback(results);
		}
		me.publish(channelName, results || {});
	};
	return callback;
};<commit_msg>Revert "Updating doc block"<commit_after>/*
 * Copyright 2014 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Note: There is a single instance of an event bus shared by all messengers

/**
 * Creates the event bus to be used for communication between widgets. If running outside of OWF,
 * If in OWF, a {{#crossLink "neon.eventing.EventBus"}}{{/crossLink}} is returned. If running inside of OWF, a
 * {{#crossLink "neon.eventing.owf.OWFEventBus"}}{{/crossLink}} is returned.
 * @return {neon.eventing.EventBus | neon.eventing.owf.OWFEventBus}
 * @private
 * @method createEventBus_
 */
neon.eventing.createEventBus_ = function () {
	if (neon.util.owfUtils.isRunningInOWF()) {
		return new neon.eventing.owf.OWFEventBus();
	}
	return new neon.eventing.EventBus();
};

/**
 * The event bus used for communication between widgets
 * @property eventBus_
 * @type {neon.eventing.EventBus|neon.eventing.owf.OWFEventBus}
 * @private
 */
neon.eventing.eventBus_ = neon.eventing.createEventBus_();

/**
 * A messenger is a widget's gateway to the event bus used for communication between widgets. Widgets publish and
 * subscribe to messages through a messenger. Each messenger has a unique id to identify which widget published the
 * message to ensure that widgets are not receiving their own messages to facilitate simpler widget code.
 * @class neon.eventing.Messenger
 * @constructor
 */

neon.eventing.Messenger = function () {
	this.id_ = uuid.v4();
};

/**
 * Publishes a message to a channel
 * @param {String} channel The channel to publish the message to
 * @param {Object} message The message to publish
 * @method publish
 */
neon.eventing.Messenger.prototype.publish = function (channel, message) {
	var data = { payload: message, sender: this.id_ };
	neon.eventing.eventBus_.publish(channel, data);
};

/**
 * Subscribes to the channel to receives any messages published to it. Note that messengers will not receive their
 * own messages even if they are subscribed to that channel.
 * @param {String} channel The channel to subscribe to
 * @param {Function} callback The callback to invoke when a message is received on the channel. The function takes
 * 1 parameter: the message that was published.
 * @method subscribe
 */
neon.eventing.Messenger.prototype.subscribe = function (channel, callback) {
	var me = this;
	neon.eventing.eventBus_.subscribe(channel, function (message) {
		if (message.sender !== me.id_) {
			callback(message, message.payload);
		}
	});
};

/**
 * Unsubscribes this messenger from the channel
 * @param {String} channel The channel to unsubscribe from
 * @method unsubscribe
 */
neon.eventing.Messenger.prototype.unsubscribe = function (channel) {
	neon.eventing.eventBus_.unsubscribe(channel);
};

/**
 * Subscribe to events notifications
 * @param callbacks {object} An object containing callback functions. Each function takes one
 * parameter, the message that was published.
 *

 *
selectionChanged - function to execute when the selection has changed

 *
filtersChanged - function to execute when the filters have been changed

 *
activeDatasetChanged - function to execute when the active dataset has changed

 *

 * @method events
 */
neon.eventing.Messenger.prototype.events = function (callbacks) {
	var me = this;
	var globalChannelConfigs = this.createGlobalChannelSubscriptions_(callbacks);
	_.each(globalChannelConfigs, function (channelConfig) {
		me.subscribe(channelConfig.channel, function (message, payload) {
				if (channelConfig.callback && typeof channelConfig.callback === 'function') {
					channelConfig.callback(message, payload);
				}
			}
		);
	});
};

neon.eventing.Messenger.prototype.createGlobalChannelSubscriptions_ = function (neonCallbacks) {
	// some of the callbacks may be null/undefined, which is ok since they will be ignored
	return [
		{channel: neon.eventing.channels.SELECTION_CHANGED, callback: neonCallbacks.selectionChanged},
		{channel: neon.eventing.channels.FILTERS_CHANGED, callback: neonCallbacks.filtersChanged},
		{channel: neon.eventing.channels.ACTIVE_DATASET_CHANGED, callback: neonCallbacks.activeDatasetChanged}
	];
};

/**
 * Adds a filter to the data being viewed, so only data matching that filter is returned from queries. This will
 * fire a filter changed event to notify other widgets that the filters have changed. If a filter with this id
 * already exists, the filter is appended to that one with an AND clause. To replace the filter entirely, use
 * {{#crossLink "neon.eventing.Messenger/replaceFilter:method"}}{{/crossLink}}.
 * @param {String} id The id of the filter to apply. A typical value to use for the id is the return value of
 * {{#crossLink "neon.widget/getInstanceId:method"}}{{/crossLink}}
 * @param {neon.query.Filter} filter The filter to add
 * @param {Function} successCallback The callback to invoke when the filter is added
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method addFilter
 */
neon.eventing.Messenger.prototype.addFilter = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('filterservice', 'addfilter'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback
		}
	);
};

/**
 * Removes a previously added filter. This will fire a filter changed event to notify other widgets that the filters
 * have changed.
 * @param {String} id The id of the filter to remove. This will be the same id used to create the filter.
 * @param {Function} successCallback The callback to invoke when the filter is removed
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method removeFilter
 */
neon.eventing.Messenger.prototype.removeFilter = function (id, successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('filterservice', 'removefilter'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback,
			data: id,
			contentType: 'text/plain',
			responseType: 'json'
		}
	);
};

/**
 * Replaces a previously added filter. This is a similar to calling remove and then add, but does so with one
 * notification event.
 * @param {String} id The id of the filter to replace.
 * @param {neon.query.Filter} filter The filter to replace the old one with
 * @param {Function} successCallback The callback to invoke when the filter is replaced
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method replaceFilter
 */
neon.eventing.Messenger.prototype.replaceFilter = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('filterservice', 'replacefilter'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback
		}
	);
};

/**
 * Clears all filters. This will fire a filter changed event to notify other widgets that the filters have changed.
 * @method clearFilters
 */
neon.eventing.Messenger.prototype.clearFilters = function (successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('filterservice', 'clearfilters'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.FILTERS_CHANGED, successCallback),
			error: errorCallback,
			responseType: 'json'
		}
	);
};

/**
 * Clears all filters but does not notify other widgets that the filters have been changed. This is useful if this is
 * part of another event that may result in the widgets updating themselves
 * @method clearFiltersSilently
 */
neon.eventing.Messenger.prototype.clearFiltersSilently = function (successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('filterservice', 'clearfilters'),
		{
			success: successCallback,
			error: errorCallback,
			responseType: 'json'
		}
	);
};

/**
 * Adds any elements matching the filter to the current selection. This will fire a selection changed event to notify
 * other widgets that the selection has changed.
 * @param {String} id The id of the selection to apply. A typical value to use for the id is the return value of
 * {{#crossLink "neon.widget/getInstanceId:method"}}{{/crossLink}}
 * @param {neon.query.Filter} filter Items matching this filter will be marked as selected
 * @param {Function} successCallback The callback to invoke when the selection is added
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method addSelection
 */
neon.eventing.Messenger.prototype.addSelection = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('selectionservice', 'addselection'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false
		}
	);
};

/**
 * Removes this filter from the selected items. This will fire a selection changed event to notify
 * other widgets that the selection has changed.
 * @param {String} id The id of the selection to remove. This will be the same id used to create the selection.
 * @param {Function} successCallback The callback to invoke when the selection is removed
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method removeSelection
 */
neon.eventing.Messenger.prototype.removeSelection = function (id, successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('selectionservice', 'removeselection'),
		{
			success:  this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false,
			data: id,
			contentType: 'text/plain',
			responseType: 'json'
		}
	);
};

/**
 * Replaces a previously added selection. This is a similar to calling remove and then add, but does so with one
 * notification event.
 * @param {String} id The id of the selection to replace.
 * @param {neon.query.Filter} filter The filter that defines the new selection
 * @param {Function} successCallback The callback to invoke when the selection is replaced
 * @param {Function} errorCallback The callback to invoke if an error occurs.
 * @method replaceFilter
 */

neon.eventing.Messenger.prototype.replaceSelection = function (id, filter, successCallback, errorCallback) {
	var filterKey = this.createFilterKey_(id, filter);
	return neon.util.ajaxUtils.doPostJSON(
		filterKey,
		neon.serviceUrl('selectionservice', 'replaceselection'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false
		}
	);
};

/**
 * Clears the currently selected items. This will fire a selection changed event to notify
 * other widgets that the selection has changed.
 * @method clearSelection
 */
neon.eventing.Messenger.prototype.clearSelection = function (successCallback, errorCallback) {
	return neon.util.ajaxUtils.doPost(
		neon.serviceUrl('selectionservice', 'clearselection'),
		{
			success: this.createChannelCallback_(neon.eventing.channels.SELECTION_CHANGED, successCallback),
			error: errorCallback,
			global: false,
			responseType: 'json'
		}
	);
};

neon.eventing.Messenger.prototype.createFilterKey_ = function (id, filter) {
	return {
		id : id,
		filter: filter
	};
};

neon.eventing.Messenger.prototype.createChannelCallback_ = function (channelName, successCallback) {
	var me = this;
	var callback = function (results) {
		// pass the results in the callback and publish them to the channel so callers can listen
		// in either place
		if (successCallback && typeof successCallback === 'function') {
			successCallback(results);
		}
		me.publish(channelName, results || {});
	};
	return callback;
};<|endoftext|>


<commit_before>var fs = require('fs');
var request = require('request');
var MATCH_DELAY = process.env.MATCH_DELAY + 3;

describe('Ladder Endpoints', function() {
    it('Regular match: 2 packets', function(done) {
        var america = fs.readFileSync(scenarios + '/TS_REGULAR_PLAYER_2');

        var options = {
            method: 'POST',
            url: url + '/ladder/ts',
            body: america.toString(),
            headers: {
                'content-type': 'text/plain',
                authorization: 'Basic dGFoajpwYXNzd29yZA=='
            }
        };

        request(options, function(err, res) {
            expect(res.statusCode).to.equal(202);

            var kaizen = fs.readFileSync(scenarios + '/TS_REGULAR_PLAYER_1');
            options.body = kaizen.toString();

            request(options, function(err, res) {
                expect(res.statusCode).to.equal(202);

                setTimeout(function() {
                    request({url: url + '/ladder/ts/player/america'}, function(err, res, body) {
                        expect(res.statusCode).to.equal(200);
                        body = JSON.parse(body);
                        expect(body.wins).to.equal(0);
                        expect(body.losses).to.equal(1);
                        expect(body.disconnects).to.equal(0);
                        expect(body.games.length).to.equal(1);
                        expect(body.points).to.equal(984);

                        request({url: url + '/ladder/ts/player/kaizen'}, function(err, res, body) {
                            expect(res.statusCode).to.equal(200);
                            body = JSON.parse(body);
                            expect(body.wins).to.equal(1);
                            expect(body.losses).to.equal(0);
                            expect(body.disconnects).to.equal(0);
                            expect(body.games.length).to.equal(1);
                            expect(body.points).to.equal(1016);
                            done();
                        });
                    });
                }, MATCH_DELAY);
            });
        });
    });

    it('D/C Scenario: 1 packet declare uploader winner', function(done) {
        var results = fs.readFileSync(scenarios + '/TS_DISCONNECT_PLAYER_1');

        var options = {
            method: 'POST',
            url: url + '/ladder/ts',
            body: results.toString(),
            headers: {
                'content-type': 'text/plain',
                authorization: 'Basic dGFoajpwYXNzd29yZA=='
            }
        };

        request(options, function(err, res) {
            expect(res.statusCode).to.equal(202);

            setTimeout(function() {
                request({url: url + '/ladder/ts/player/xy'}, function(err, res, body) {
                    expect(res.statusCode).to.equal(200);
                    body = JSON.parse(body);
                    expect(body.wins).to.equal(0);
                    expect(body.losses).to.equal(1);
                    expect(body.disconnects).to.equal(1);
                    expect(body.games.length).to.equal(1);
                    expect(body.points).to.equal(984);

                    request({url: url + '/ladder/ts/player/test2'}, function(err, res, body) {
                        expect(res.statusCode).to.equal(200);
                        body = JSON.parse(body);
                        expect(body.wins).to.equal(1);
                        expect(body.losses).to.equal(0);
                        expect(body.disconnects).to.equal(0);
                        expect(body.games.length).to.equal(1);
                        expect(body.points).to.equal(1016);
                        done();
                    });
                });
            }, MATCH_DELAY);
        });
    });

    it('should provide leaderboard for players', function(done) {
        /* warm up cache */
        request(url + '/ladder/ts', function() {

            /* actual results */
            setTimeout(function() {
                request(url + '/ladder/ts', function(err, res, body) {
                    expect(res.statusCode).to.equal(200);
                    body = JSON.parse(body);
                    expect(body.length).to.equal(4);
                    done();
                });
            }, MATCH_DELAY + 5);

        });
    });

    it('should provide rank for individiual players', function(done) {
        request(url + '/ladder/ts/player/kaizen', function(err, res, body) {
           expect(res.statusCode).to.equal(200);
            body = JSON.parse(body);
            expect(body.rank).to.equal(1);
            done();
        });
    });

});<commit_msg>logic clean up<commit_after>var fs = require('fs');
var request = require('request');
var MATCH_DELAY = process.env.MATCH_DELAY + 3;

describe('Ladder Endpoints', function() {
    it('Regular match: 2 packets', function(done) {
        var america = fs.readFileSync(scenarios + '/TS_REGULAR_PLAYER_2');

        var options = {
            method: 'POST',
            url: url + '/ladder/ts',
            body: america.toString(),
            headers: {
                'content-type': 'text/plain',
                authorization: 'Basic dGFoajpwYXNzd29yZA=='
            }
        };

        request(options, function(err, res) {
            expect(res.statusCode).to.equal(202);

            var kaizen = fs.readFileSync(scenarios + '/TS_REGULAR_PLAYER_1');
            options.body = kaizen.toString();

            request(options, function(err, res) {
                expect(res.statusCode).to.equal(202);

                setTimeout(function() {
                    request({url: url + '/ladder/ts/player/america'}, function(err, res, body) {
                        expect(res.statusCode).to.equal(200);
                        body = JSON.parse(body);
                        expect(body.wins).to.equal(0);
                        expect(body.losses).to.equal(1);
                        expect(body.disconnects).to.equal(0);
                        expect(body.games.length).to.equal(1);
                        expect(body.points).to.equal(984);

                        request({url: url + '/ladder/ts/player/kaizen'}, function(err, res, body) {
                            expect(res.statusCode).to.equal(200);
                            body = JSON.parse(body);
                            expect(body.wins).to.equal(1);
                            expect(body.losses).to.equal(0);
                            expect(body.disconnects).to.equal(0);
                            expect(body.games.length).to.equal(1);
                            expect(body.points).to.equal(1016);
                            done();
                        });
                    });
                }, MATCH_DELAY);
            });
        });
    });

    it('D/C Scenario: 1 packet declare uploader winner', function(done) {
        var results = fs.readFileSync(scenarios + '/TS_DISCONNECT_PLAYER_1');

        var options = {
            method: 'POST',
            url: url + '/ladder/ts',
            body: results.toString(),
            headers: {
                'content-type': 'text/plain',
                authorization: 'Basic dGFoajpwYXNzd29yZA=='
            }
        };

        request(options, function(err, res) {
            expect(res.statusCode).to.equal(202);

            setTimeout(function() {
                request({url: url + '/ladder/ts/player/xy'}, function(err, res, body) {
                    expect(res.statusCode).to.equal(200);
                    body = JSON.parse(body);
                    expect(body.wins).to.equal(0);
                    expect(body.losses).to.equal(1);
                    expect(body.disconnects).to.equal(1);
                    expect(body.games.length).to.equal(1);
                    expect(body.points).to.equal(984);

                    request({url: url + '/ladder/ts/player/test2'}, function(err, res, body) {
                        expect(res.statusCode).to.equal(200);
                        body = JSON.parse(body);
                        expect(body.wins).to.equal(1);
                        expect(body.losses).to.equal(0);
                        expect(body.disconnects).to.equal(0);
                        expect(body.games.length).to.equal(1);
                        expect(body.points).to.equal(1016);
                        done();
                    });
                });
            }, MATCH_DELAY);
        });
    });

    it('should provide leaderboard for players', function(done) {
        /* warm up cache */
        request(url + '/ladder/ts', function() {});

        /* actual results */
        setTimeout(function() {
            request(url + '/ladder/ts', function(err, res, body) {
                expect(res.statusCode).to.equal(200);
                body = JSON.parse(body);
                expect(body.length).to.equal(4);
                done();
            });
        }, MATCH_DELAY + 3);
    });

    it('should provide rank for individiual players', function(done) {
        request(url + '/ladder/ts/player/kaizen', function(err, res, body) {
           expect(res.statusCode).to.equal(200);
            body = JSON.parse(body);
            expect(body.rank).to.equal(1);
            done();
        });
    });

});<|endoftext|>


<commit_before>/*
 * NTwitBot - retrieve.test.js
 * Copyright (C) 2016-2017 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const Retrieve  = require('../../lib/retrieve.js');
const Twitter   = require('../../lib/twitter.js');
const Util      = require('../../lib/util.js');

const retriever = new Retrieve(new Twitter(), new Util());

describe('Retrieve', () => {
    afterEach(() => {
        jest.restoreAllMocks();
    });

    describe('Mention Retrievals', () => {
        it('should retrieve all mentions of the user', () => {
            const getMentionsSpy =
                jest.spyOn(retriever.twitterHandler, 'getMentions').mockImplementation(() => Promise.resolve({}));

            expect.assertions(2);

            return retriever.retrieveMentions(0).then((mentions) => {
                expect(mentions).toBeDefined();
                expect(getMentionsSpy).toHaveBeenCalledWith({ count: 200 });
            });
        });

        it('should retrieve all mentions since a specified ID of the user', () => {
            const getMentionsSpy =
                jest.spyOn(retriever.twitterHandler, 'getMentions').mockImplementation(() => Promise.resolve({}));

            expect.assertions(2);

            return retriever.retrieveMentions('3423232321').then((mentions) => {
                expect(mentions).toBeDefined();
                expect(getMentionsSpy).toHaveBeenCalledWith({ since_id: '3423232321' });
            });
        });
    });

    describe('Tweet Retrievals', () => {
        it('should specify the correct request for tweets from new users', () => {
            const users = { '4243675': 0, '4243655': 0 };
            const retrieveForNewSpy =
                jest.spyOn(retriever, 'retrieveForNew').mockImplementation(() => Promise.resolve({}));

            expect.assertions(4);

            return retriever.retrieveTweets(users).then((tweets) => {
                expect(tweets).toBeDefined();
                expect(retrieveForNewSpy).toHaveBeenCalledTimes(2);
                expect(retrieveForNewSpy).toHaveBeenCalledWith({ user_id: '4243675', trim_user: true });
                expect(retrieveForNewSpy).toHaveBeenCalledWith({ user_id: '4243655', trim_user: true });
            });
        });

        it('should specify the correct request for tweets from existing users', () => {
            const users = { '4243675': '2324145663412', '4243655': '54643414213123' };
            const retrieveForExistingSpy =
                jest.spyOn(retriever, 'retrieveForExisting').mockImplementation(() => Promise.resolve({}));

            expect.assertions(4);

            return retriever.retrieveTweets(users).then((tweets) => {
                expect(tweets).toBeDefined();
                expect(retrieveForExistingSpy).toHaveBeenCalledTimes(2);
                expect(retrieveForExistingSpy).toHaveBeenCalledWith({
                    user_id: '4243675',
                    since_id: '2324145663412',
                    count: 200,
                    trim_user: true
                });
                expect(retrieveForExistingSpy).toHaveBeenCalledWith({
                    user_id: '4243655',
                    since_id: '54643414213123',
                    count: 200,
                    trim_user: true
                });
            });
        });
    });
});
<commit_msg>Clean up tests for retrieve<commit_after>/*
 * NTwitBot - retrieve.test.js
 * Copyright (C) 2016-2017 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const Retrieve  = require('../../lib/retrieve.js');
const Twitter   = require('../../lib/twitter.js');
const Util      = require('../../lib/util.js');

const utils = new Util();
const retriever = new Retrieve(new Twitter({}, utils), utils);

describe('Retrieve', () => {

    describe('Mention Retrievals', () => {
        const getMentionsSpy = jest.spyOn(retriever.twitterHandler, 'getMentions');

        afterEach(() => {
            getMentionsSpy.mockReset();
        });

        it('should retrieve all mentions of the user', () => {
            getMentionsSpy.mockImplementation(() => Promise.resolve({}));

            return retriever.retrieveMentions(0).then((mentions) => {
                expect(mentions).toBeDefined();
                expect(getMentionsSpy).toHaveBeenCalledWith({ count: 200 });
            });
        });

        it('should retrieve all mentions since a specified ID of the user', () => {
            getMentionsSpy.mockImplementation(() => Promise.resolve({}));

            return retriever.retrieveMentions('5001').then((mentions) => {
                expect(mentions).toBeDefined();
                expect(getMentionsSpy).toHaveBeenCalledWith({ since_id: '5001' });
            });
        });
    });

    describe('Tweet Retrievals', () => {

        describe('Existing Users', () => {
            let retrieveForExistingSpy;

            beforeEach(() => {
                retrieveForExistingSpy = jest.spyOn(retriever, 'retrieveForExisting');
            });

            afterEach(() => {
                retrieveForExistingSpy.mockRestore();
            });

            it('should specify the correct parameters for a retrieval', () => {
                const users = { '001': '5001', '002': '5002' };
                retrieveForExistingSpy.mockImplementation(() => Promise.resolve({}));

                return retriever.retrieveTweets(users).then((tweets) => {
                    expect(tweets).toEqual([ {}, {} ]);
                    expect(retrieveForExistingSpy).toHaveBeenCalledTimes(2);
                    expect(retrieveForExistingSpy).toHaveBeenCalledWith({
                        user_id: '001',
                        since_id: '5001',
                        count: 200,
                        trim_user: true
                    });
                    expect(retrieveForExistingSpy).toHaveBeenCalledWith({
                        user_id: '002',
                        since_id: '5002',
                        count: 200,
                        trim_user: true
                    });
                });
            });
        });

        describe('New Users', () => {
            let retrieveForNewSpy;

            beforeEach(() => {
                retrieveForNewSpy = jest.spyOn(retriever, 'retrieveForNew');
            });

            afterEach(() => {
                retrieveForNewSpy.mockRestore();
            });

            it('should specify the correct parameters for a retrieval', () => {
                const users = { '001': 0, '002': 0 };
                retrieveForNewSpy.mockImplementation(() => Promise.resolve({}));

                return retriever.retrieveTweets(users).then((tweets) => {
                    expect(tweets).toEqual([ {}, {} ]);
                    expect(retrieveForNewSpy).toHaveBeenCalledTimes(2);
                    expect(retrieveForNewSpy).toHaveBeenCalledWith({
                        user_id: '001',
                        trim_user: true
                    });
                    expect(retrieveForNewSpy).toHaveBeenCalledWith({
                        user_id: '002',
                        trim_user: true
                    });
                });
            });
        });

    });
});
<|endoftext|>


<commit_before>function Painter() {
  this.fps = 60;
  this.svg = null;
  this.$svg = $();
  this.width = null;
  this.height = null;
  this.nodeWidth = null;
  this.nodeHeight = null;
  this.margin = null;
  this.lineHeight = null;
  this.labels = [
    'SN min',
    'SN max',
    'SN next',
    'Timer',
    '',
    'Protocol',
    'W',
    'a',
    'Timeout',
    'P',
    'Utilization',
    'Time',
    '',
    'RN min'
  ];

  this._init();
}

Painter.prototype._init = function () {
  var self = this,
      resizeTimer,
      callback = function () {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(function () {
          self._updateDimension();
          self.drawAll();
        }, 250);
      };
  $(window).resize(callback);
  $('[data-toggle="collapse"]').click(callback);
  this._drawLegend();
};

Painter.prototype._updateDimension = function () {
  var offset = this.$svg.offset();
  this.width = this.$svg.width();
  this.height = Math.min(
      Math.min(Math.max($(window).height() - offset.top - 40, 320), 480),
      this.width);
  this.$svg.height(this.height);
  this.nodeWidth = Math.min(Math.max(this.width / 3, 100), 240);
  this.nodeHeight = this.height / 10;
  this.margin = Math.max(this.width / 5 - 40, 10);
  this.lineHeight = this.height / 16;
};

Painter.prototype._drawLegend = function () {
  var defaultWidth = 100,
      defaultHeight = 30,
      createSvg = function (selector, classes) {
        var svg = d3.select(selector)
          .append('svg')
          .attr('width', defaultWidth)
          .attr('height', defaultHeight)
          .append('g')
          .classed(classes[0], true)
          .attr('transform', 'translate(' + (defaultWidth / 2) + ',' +
            (defaultHeight / 2) + ')');
        if (classes[1]) {
          svg = svg.append('g').classed(classes[1], true);
        }
        return svg;
      },
      addRect = function (svg, options) {
        var rect = svg.append('rect')
          .attr('x', -(defaultWidth / 2))
          .attr('y', -((options.height || defaultHeight) / 2))
          .attr('width', defaultWidth)
          .attr('height', options.height || defaultHeight);
        if (options.rx) {
          rect.attr('rx', options.rx).attr('ry', options.ry);
        }
        return rect;
      },
      addText = function (svg, text) {
        return svg.append('text').text(text);
      },
      createSymbol = function (options) {
        var svg = createSvg(options.selector, options.classes);
        addRect(svg, options);
        addText(svg, options.text);
      }
  createSymbol({
    selector: '#legend .node',
    classes: ['nodes'],
    text: 'Name',
    rx: 5,
    ry: 5
  });
  createSymbol({
    selector: '#legend .frame',
    classes: ['data-frames'],
    text: 'SN'
  });
  createSymbol({
    selector: '#legend .frame.error',
    classes: ['data-frames', 'error'],
    text: 'SN'
  });
  createSymbol({
    selector: '#legend .ack',
    classes: ['control-frames'],
    text: 'RR RN',
    height: defaultHeight / 2
  });
  createSymbol({
    selector: '#legend .nack',
    classes: ['control-frames'],
    text: 'REJ RN',
    height: defaultHeight / 2
  });
};

Painter.prototype.setSystem = function (system) {
  this.system = system;
  this.protocol = this.system.node1.constructor == GbnNode ? 'GBN' : 'SR';
  $('#display').empty();
  this.svg = d3.select('#display')
      .append('div')
      .classed('svg-container', true)
      .append('svg')
      .attr('width', "100%")
      .attr('height', this.height);
  this.$svg = $('#display svg');
  this._updateDimension();
  this.svg.append('g').classed('data-frames', true);
  this.svg.append('g').classed('control-frames', true);
  this.svg.append('g').classed('nodes', true);
  this.svg.append('g').classed('values', true);
};

Painter.prototype.setFps = function (fps) {
  this.fps = fps;
};

Painter.prototype.drawAll = function () {
  this._drawNodes();
  this._drawPrimaryLink();
  this._drawSecondaryLink();
  this._displayValues();
};

Painter.prototype._drawNodes = function () {
  var self = this,
      dx = this.margin + this.nodeWidth / 2,
      nodes = this.svg.select('.nodes')
        .selectAll('g')
        .data([this.system.node1, this.system.node2]),
      nodesEnter = nodes.enter().append('g');
  nodesEnter.append('rect')
      .attr('rx', 5)
      .attr('ry', 5);
  nodesEnter.append('text')
      .text(function (d) { return d.name; });
  nodes.attr('transform', function (d, i) {
    var dy = (i * self.height);
    return 'translate(' + dx + ',' + dy + ')';
  });
  nodes.select('rect')
      .attr('x', -(this.nodeWidth / 2))
      .attr('y', -(this.nodeHeight))
      .attr('width', this.nodeWidth)
      .attr('height', this.nodeHeight * 2);
  nodes.select('text')
      .attr('y', function (d, i) {
        return self.nodeHeight / 2 - self.nodeHeight * i;
      });
};

Painter.prototype._drawPrimaryLink = function () {
  var self = this,
      system = this.system,
      currentTime = system.clock.currentTime,
      w = this.nodeWidth / 3,
      h = (this.height - this.nodeHeight * 2) / system.params.a,
      dx = self.margin + self.nodeWidth / 4,
      frames = this.svg.select('.data-frames')
        .selectAll('g')
        .data(system.link1.queue, function (d) { return d.time; }),
      framesEnter = frames.enter()
        .append('g')
        .classed('error', function (d) { return d.error; });
  framesEnter.append('rect');
  if (h > 3) {
    framesEnter.append('text')
        .text(function (d) { return d.sn; });
  }
  frames.attr('transform', function (d, i) {
    var dy = self.nodeHeight + (currentTime - d.time) * h + h / 2;
    return 'translate(' + dx + ',' + dy + ')';
  });
  frames.select('rect')
      .attr('stroke-width', Math.min(Math.max(h / 20 - 0.25, 0), 1))
      .attr('y', -(h / 2))
      .attr('height', h)
      .attr('x', -(w / 2))
      .attr('width', w);
  if (h > 3) {
    frames.select('text').attr('font-size', Math.min(h * 2 / 3, 14));
  } else {
    frames.select('text').remove();
  }
  frames.exit().remove();
};

Painter.prototype._drawSecondaryLink = function () {
  var self = this,
      system = this.system,
      currentTime = system.clock.currentTime,
      w = this.nodeWidth / 3,
      h = (this.height - this.nodeHeight * 2) / system.params.a / 3,
      dx = this.margin + this.nodeWidth - this.nodeWidth / 4,
      frames = this.svg.select('.control-frames')
        .selectAll('g')
        .data(system.link2.queue, function (d) { return d.time; }),
      framesEnter = frames.enter()
        .append('g');
  framesEnter.append('rect');
  if (h > 3) {
    framesEnter.append('text')
        .text(function (d) { return d.func + ' ' + d.rn; });
  }
  frames.attr('transform', function (d, i) {
    var dy = (self.height - self.nodeHeight) - (currentTime - d.time) * h * 3;
    return 'translate(' + dx + ',' + dy + ')';
  });
  frames.select('rect')
      .attr('y', -(h / 2))
      .attr('height', h)
      .attr('x', -(w / 2))
      .attr('width', w);
  if (h > 3) {
    frames.select('text').attr('font-size', Math.min(h * 2, 14));
  } else {
    frames.select('text').remove();
  }
  frames.exit().remove();
};

Painter.prototype._displayValues = function () {
  var self = this,
      system = this.system,
      sender = system.node1,
      receiver = system.node2,
      currentTime = system.clock.currentTime,
      x = this.width * 6 / 13 + (this.margin + this.nodeWidth) * 7 / 13,
      values = this.svg.select('.values')
        .selectAll('g')
        .data([
          sender.txbase % sender.s,
          (sender.txbase + sender.txbuf.length - 1) % sender.s,
          sender.txnext % sender.s,
          Math.floor(currentTime - sender.txtimers.get(0)),
          '',
          this.protocol,
          sender.w,
          sender.a,
          sender.txtimeout,
          system.link1.currentFrameErrorRate().toFixed(6),
          system.node2.currentUtilization().toFixed(6),
          currentTime.toPrecision(3),
          '',
          receiver.rxbase % receiver.s
        ]);
  var valuesEnter = values.enter().append('g');
  valuesEnter.append('text')
      .classed('name', true)
      .text(function (d, i) { return self.labels[i]; });
  valuesEnter.append('text')
      .classed('value', true);
  values.select('.name')
      .attr('x', x - 5)
      .attr('y', function (d, i) {
        return (i + 1.5) * self.lineHeight;
      });
  values.select('.value')
      .text(function (d) { return d; })
      .attr('x', x + 5)
      .attr('y', function (d, i) {
        return (i + 1.5) * self.lineHeight;
      });
};
<commit_msg>Remove timer from display<commit_after>function Painter() {
  this.fps = 60;
  this.svg = null;
  this.$svg = $();
  this.width = null;
  this.height = null;
  this.nodeWidth = null;
  this.nodeHeight = null;
  this.margin = null;
  this.lineHeight = null;
  this.labels = [
    'SN min',
    'SN max',
    'SN next',
    '',
    'Protocol',
    'W',
    'a',
    'Timeout',
    'P',
    'Utilization',
    'Time',
    '',
    'RN min'
  ];

  this._init();
}

Painter.prototype._init = function () {
  var self = this,
      resizeTimer,
      callback = function () {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(function () {
          self._updateDimension();
          self.drawAll();
        }, 250);
      };
  $(window).resize(callback);
  $('[data-toggle="collapse"]').click(callback);
  this._drawLegend();
};

Painter.prototype._updateDimension = function () {
  var offset = this.$svg.offset();
  this.width = this.$svg.width();
  this.height = Math.min(
      Math.min(Math.max($(window).height() - offset.top - 40, 320), 480),
      this.width);
  this.$svg.height(this.height);
  this.nodeWidth = Math.min(Math.max(this.width / 3, 100), 240);
  this.nodeHeight = this.height / 10;
  this.margin = Math.max(this.width / 5 - 40, 10);
  this.lineHeight = this.height / 16;
};

Painter.prototype._drawLegend = function () {
  var defaultWidth = 100,
      defaultHeight = 30,
      createSvg = function (selector, classes) {
        var svg = d3.select(selector)
          .append('svg')
          .attr('width', defaultWidth)
          .attr('height', defaultHeight)
          .append('g')
          .classed(classes[0], true)
          .attr('transform', 'translate(' + (defaultWidth / 2) + ',' +
            (defaultHeight / 2) + ')');
        if (classes[1]) {
          svg = svg.append('g').classed(classes[1], true);
        }
        return svg;
      },
      addRect = function (svg, options) {
        var rect = svg.append('rect')
          .attr('x', -(defaultWidth / 2))
          .attr('y', -((options.height || defaultHeight) / 2))
          .attr('width', defaultWidth)
          .attr('height', options.height || defaultHeight);
        if (options.rx) {
          rect.attr('rx', options.rx).attr('ry', options.ry);
        }
        return rect;
      },
      addText = function (svg, text) {
        return svg.append('text').text(text);
      },
      createSymbol = function (options) {
        var svg = createSvg(options.selector, options.classes);
        addRect(svg, options);
        addText(svg, options.text);
      }
  createSymbol({
    selector: '#legend .node',
    classes: ['nodes'],
    text: 'Name',
    rx: 5,
    ry: 5
  });
  createSymbol({
    selector: '#legend .frame',
    classes: ['data-frames'],
    text: 'SN'
  });
  createSymbol({
    selector: '#legend .frame.error',
    classes: ['data-frames', 'error'],
    text: 'SN'
  });
  createSymbol({
    selector: '#legend .ack',
    classes: ['control-frames'],
    text: 'RR RN',
    height: defaultHeight / 2
  });
  createSymbol({
    selector: '#legend .nack',
    classes: ['control-frames'],
    text: 'REJ RN',
    height: defaultHeight / 2
  });
};

Painter.prototype.setSystem = function (system) {
  this.system = system;
  this.protocol = this.system.node1.constructor == GbnNode ? 'GBN' : 'SR';
  $('#display').empty();
  this.svg = d3.select('#display')
      .append('div')
      .classed('svg-container', true)
      .append('svg')
      .attr('width', "100%")
      .attr('height', this.height);
  this.$svg = $('#display svg');
  this._updateDimension();
  this.svg.append('g').classed('data-frames', true);
  this.svg.append('g').classed('control-frames', true);
  this.svg.append('g').classed('nodes', true);
  this.svg.append('g').classed('values', true);
};

Painter.prototype.setFps = function (fps) {
  this.fps = fps;
};

Painter.prototype.drawAll = function () {
  this._drawNodes();
  this._drawPrimaryLink();
  this._drawSecondaryLink();
  this._displayValues();
};

Painter.prototype._drawNodes = function () {
  var self = this,
      dx = this.margin + this.nodeWidth / 2,
      nodes = this.svg.select('.nodes')
        .selectAll('g')
        .data([this.system.node1, this.system.node2]),
      nodesEnter = nodes.enter().append('g');
  nodesEnter.append('rect')
      .attr('rx', 5)
      .attr('ry', 5);
  nodesEnter.append('text')
      .text(function (d) { return d.name; });
  nodes.attr('transform', function (d, i) {
    var dy = (i * self.height);
    return 'translate(' + dx + ',' + dy + ')';
  });
  nodes.select('rect')
      .attr('x', -(this.nodeWidth / 2))
      .attr('y', -(this.nodeHeight))
      .attr('width', this.nodeWidth)
      .attr('height', this.nodeHeight * 2);
  nodes.select('text')
      .attr('y', function (d, i) {
        return self.nodeHeight / 2 - self.nodeHeight * i;
      });
};

Painter.prototype._drawPrimaryLink = function () {
  var self = this,
      system = this.system,
      currentTime = system.clock.currentTime,
      w = this.nodeWidth / 3,
      h = (this.height - this.nodeHeight * 2) / system.params.a,
      dx = self.margin + self.nodeWidth / 4,
      frames = this.svg.select('.data-frames')
        .selectAll('g')
        .data(system.link1.queue, function (d) { return d.time; }),
      framesEnter = frames.enter()
        .append('g')
        .classed('error', function (d) { return d.error; });
  framesEnter.append('rect');
  if (h > 3) {
    framesEnter.append('text')
        .text(function (d) { return d.sn; });
  }
  frames.attr('transform', function (d, i) {
    var dy = self.nodeHeight + (currentTime - d.time) * h + h / 2;
    return 'translate(' + dx + ',' + dy + ')';
  });
  frames.select('rect')
      .attr('stroke-width', Math.min(Math.max(h / 20 - 0.25, 0), 1))
      .attr('y', -(h / 2))
      .attr('height', h)
      .attr('x', -(w / 2))
      .attr('width', w);
  if (h > 3) {
    frames.select('text').attr('font-size', Math.min(h * 2 / 3, 14));
  } else {
    frames.select('text').remove();
  }
  frames.exit().remove();
};

Painter.prototype._drawSecondaryLink = function () {
  var self = this,
      system = this.system,
      currentTime = system.clock.currentTime,
      w = this.nodeWidth / 3,
      h = (this.height - this.nodeHeight * 2) / system.params.a / 3,
      dx = this.margin + this.nodeWidth - this.nodeWidth / 4,
      frames = this.svg.select('.control-frames')
        .selectAll('g')
        .data(system.link2.queue, function (d) { return d.time; }),
      framesEnter = frames.enter()
        .append('g');
  framesEnter.append('rect');
  if (h > 3) {
    framesEnter.append('text')
        .text(function (d) { return d.func + ' ' + d.rn; });
  }
  frames.attr('transform', function (d, i) {
    var dy = (self.height - self.nodeHeight) - (currentTime - d.time) * h * 3;
    return 'translate(' + dx + ',' + dy + ')';
  });
  frames.select('rect')
      .attr('y', -(h / 2))
      .attr('height', h)
      .attr('x', -(w / 2))
      .attr('width', w);
  if (h > 3) {
    frames.select('text').attr('font-size', Math.min(h * 2, 14));
  } else {
    frames.select('text').remove();
  }
  frames.exit().remove();
};

Painter.prototype._displayValues = function () {
  var self = this,
      system = this.system,
      sender = system.node1,
      receiver = system.node2,
      currentTime = system.clock.currentTime,
      x = this.width * 6 / 13 + (this.margin + this.nodeWidth) * 7 / 13,
      values = this.svg.select('.values')
        .selectAll('g')
        .data([
          sender.txbase % sender.s,
          (sender.txbase + sender.txbuf.length - 1) % sender.s,
          sender.txnext % sender.s,
          '',
          this.protocol,
          sender.w,
          sender.a,
          sender.txtimeout,
          system.link1.currentFrameErrorRate().toFixed(6),
          system.node2.currentUtilization().toFixed(6),
          currentTime.toPrecision(3),
          '',
          receiver.rxbase % receiver.s
        ]);
  var valuesEnter = values.enter().append('g');
  valuesEnter.append('text')
      .classed('name', true)
      .text(function (d, i) { return self.labels[i]; });
  valuesEnter.append('text')
      .classed('value', true);
  values.select('.name')
      .attr('x', x - 5)
      .attr('y', function (d, i) {
        return (i + 2) * self.lineHeight;
      });
  values.select('.value')
      .text(function (d) { return d; })
      .attr('x', x + 5)
      .attr('y', function (d, i) {
        return (i + 2) * self.lineHeight;
      });
};
<|endoftext|>


<commit_before>'use strict';

/*
 * This seed create data for demo section:
 *   3 category, each category contain 10 sections
 *   last section in 3rd category is empty
 *   first section contain 200 topics, all others only one
 *   first topic in first topic contain 100 post, all others only one
 *
 */

const _         = require('lodash');
const co        = require('co');
const thenify   = require('thenify');
const charlatan = require('charlatan');
const ObjectId  = require('mongoose').Types.ObjectId;

let Category;
let Section;
let Topic;
let Post;
let User;
let Vote;
let UserGroup;
let settings;
let parser;

const CATEGORY_COUNT = 3;
const SECTION_COUNT  = 10;
const SUB_SECTION_DEEP = 1;
const TOPIC_COUNT_IN_BIG_SECTION  = 200;
const POST_COUNT_IN_BIG_TOPIC  = 100;
const USER_COUNT = 200;
const MAX_MODERATOR_COUNT = 3;
const MAX_SUB_SECTION_COUNT = 3;
const MAX_VOTES = 10;

let display_order = 0;

function getNextDisplayOrder() {
  display_order++;
  return display_order;
}

let users = [];
let postDay = 0;

const createPost = co.wrap(function* (topic, reply_to) {
  let md = charlatan.Lorem.paragraphs(charlatan.Helpers.rand(5, 1)).join('\n\n');
  let user = users[charlatan.Helpers.rand(USER_COUNT)];

  let options = yield settings.getByCategory('forum_markup', { usergroup_ids: user.usergroups }, { alias: true });

  let result = yield parser({
    text: md,
    attachments: [],
    options
  });

  let ts = new Date(2010, 0, postDay++);

  let post = new Post({
    _id: new ObjectId(Math.round(ts / 1000)),

    html:    result.html,
    md,

    st:      Post.statuses.VISIBLE,
    topic:   topic._id,

    user,

    /*eslint-disable new-cap*/
    ip:      charlatan.Internet.IPv4(),

    /*eslint-disable no-undefined*/
    to:      reply_to ? reply_to._id  : undefined,
    to_user: reply_to ? reply_to.user : undefined,
    to_phid: reply_to ? reply_to.hid  : undefined,

    ts,

    params:  settings
  });

  yield post.save();
  yield User.update({ _id: post.user }, { $inc: { post_count: 1 } });

  return post;
});

const addVotes = co.wrap(function* (post) {
  let votes = 0;

  for (let i = charlatan.Helpers.rand(MAX_VOTES); i > 0; i--) {
    let user = users[charlatan.Helpers.rand(USER_COUNT)];
    let value = Math.random() > 0.5 ? 1 : -1;

    let vote = new Vote({
      to:     post.user,
      from:   user._id,
      'for':  post._id,
      type:   Vote.types.FORUM_POST,
      value
    });

    votes += value;

    yield vote.save();
  }

  yield post.update({ votes });
});

const createTopic = co.wrap(function* (section, post_count) {
  let first_post;
  let last_post;

  let topic = new Topic({
    _id: new ObjectId(Math.round(new Date(2010, 0, postDay) / 1000)),

    title: charlatan.Lorem.sentence().slice(0, -1),

    st: Topic.statuses.OPEN,
    section: section._id,

    views_count: charlatan.Helpers.rand(1000)
  });

  // Save topic to the database before creating posts,
  // it's needed because of Post model hooks
  //
  yield topic.save();

  let posts = [];

  for (let i = 0; i < post_count; i++) {
    // 50% posts won't have any reply information, 25% posts will be
    // answers to the previous post, 12.5% posts will be answers to the
    // 2nd last post and so on.
    //
    let reply_id = posts.length - Math.floor(1 / Math.random()) + 1;
    let reply_to = posts[reply_id];

    var post = yield createPost(topic, reply_to);

    if (!first_post) {
      first_post = post;
    }

    last_post = post;

    posts.push(post);

    yield addVotes(post);
  }

  topic.cache.post_count = post_count;

  topic.cache.first_post = first_post._id;
  topic.cache.first_ts = first_post.ts;
  topic.cache.first_user = first_post.user;

  topic.cache.last_post = last_post._id;
  topic.cache.last_ts = last_post.ts;
  topic.cache.last_user = last_post.user;

  _.assign(topic.cache_hb, topic.cache);

  // Update cache for this topic
  //
  yield topic.save();
});

const createSection = co.wrap(function* (category, sub_section_deep) {
  let section = new Section({
    title: charlatan.Lorem.sentence(charlatan.Helpers.rand(5, 3)).slice(0, -1),
    description: charlatan.Lorem.sentence(),

    parent: category._id,
    display_order: getNextDisplayOrder(),

    cache: {
      real: {}
    }
  });

  yield section.save();

  // add sub-sections
  if (!sub_section_deep || charlatan.Helpers.rand(3) === 2) {
    return;
  }

  for (let i = charlatan.Helpers.rand(MAX_SUB_SECTION_COUNT); i > 0; i--) {
    yield createSection(section, sub_section_deep - 1);
  }
});

const createUsers = co.wrap(function* () {
  let userGroupsByName = {};
  let groups = yield UserGroup.find().select('_id short_name');

  // collect usergroups
  groups.forEach(function (group) {
    userGroupsByName[group.short_name] = group;
  });

  for (let i = 0; i < USER_COUNT; i++) {
    let user = new User({
      first_name: charlatan.Name.firstName(),
      last_name:  charlatan.Name.lastName(),
      nick:       charlatan.Internet.userName(),
      email:      charlatan.Internet.email(),
      joined_ts:  new Date(),
      usergroups: userGroupsByName.members
    });

    yield user.save();

    // add user to store
    users.push(user);
  }
});

let createSections = co.wrap(function* () {
  for (let i = 0; i < CATEGORY_COUNT; i++) {
    let category = new Category({
      title: charlatan.Lorem.sentence(charlatan.Helpers.rand(5, 3)).slice(0, -1),
      description: charlatan.Lorem.sentence(),

      display_order: getNextDisplayOrder('display_order'),
      is_category: true
    });

    yield category.save();

    // create sections
    for (let j = 0; j < SECTION_COUNT; j++) {
      yield createSection(category, SUB_SECTION_DEEP);
    }
  }
});

const updateSectionStat = co.wrap(function* (section) {
  let topicCount;
  let postCount;

  // Clear getSectionTree cache (used in both `updateCache` and `getChildren`
  // functions below).
  //
  Section.getChildren.clear();

  yield Section.updateCache(section._id, true);

  let sections = yield Section.getChildren(section._id, -1);

  // aggregation doesn't return a promise in mongoose?
  let sum = yield thenify(callback => Topic.aggregate(
    { $match: {
      section: { $in: _.map(sections.concat([ section ]), '_id') }
    } },
    { $group: {
      _id: null,
      topic_count: { $sum: 1 },
      post_count: { $sum: '$cache.post_count' }
    } }).exec(callback));

  if (sum && sum[0]) {
    postCount  = sum[0].post_count;
    topicCount = sum[0].topic_count;
  } else {
    // no topics found in section or any of its subsections
    postCount  = 0;
    topicCount = 0;
  }

  section.cache.post_count = postCount;
  section.cache.topic_count = topicCount;

  section.cache_hb.post_count = postCount;
  section.cache_hb.topic_count = topicCount;

  yield section.save();
});

const createTopics = co.wrap(function* () {
  let sections = yield Section.find({ is_category: false })
                              .select('_id cache')
                              .sort({ hid: -1 })
                              .skip(1);

  for (let i = 0; i < sections.length; i++) {
    let section = sections[i];

    // create topic with single post
    yield createTopic(section, 1);
    yield updateSectionStat(section);
  }
});

const fillBigSection = co.wrap(function* () {
  let section = yield Section.findOne({ is_category: false })
                             .sort({ hid: 1 });

  for (let i = 0; i < TOPIC_COUNT_IN_BIG_SECTION; i++) {
    yield createTopic(section, 1);
  }

  yield updateSectionStat(section);
});

const addBigTopic = co.wrap(function* () {
  let section = yield Section.findOne({ is_category: false })
                             .sort({ hid: 1 });

  yield createTopic(section, POST_COUNT_IN_BIG_TOPIC);
  yield updateSectionStat(section);
});

const addModerators = co.wrap(function* () {
  let SectionModeratorStore = settings.getStore('section_moderator');

  if (!SectionModeratorStore) {
    throw new Error('Settings store `section_moderator` is not registered.');
  }

  let sections = yield Section.find({ is_category: false }).select('_id');

  for (let i = 0; i < sections.length; i++) {
    let section = sections[i];

    for (let j = charlatan.Helpers.rand(MAX_MODERATOR_COUNT); j > 0; j--) {
      let user = users[charlatan.Helpers.rand(USER_COUNT)];

      yield SectionModeratorStore.set(
        { forum_mod_visible: { value: true } },
        { section_id: section._id, user_id: user._id }
      );
    }
  }
});

module.exports = co.wrap(function* (N) {
  Category  = N.models.forum.Section;
  Section   = N.models.forum.Section;
  Topic     = N.models.forum.Topic;
  Post      = N.models.forum.Post;
  User      = N.models.users.User;
  UserGroup = N.models.users.UserGroup;
  Vote      = N.models.users.Vote;
  settings  = N.settings;
  parser    = thenify(N.parse);

  yield createUsers();
  yield createSections();
  yield createTopics();
  yield fillBigSection();
  yield addBigTopic();
  yield addModerators();
});
<commit_msg>Removed redundant wrapper<commit_after>'use strict';

/*
 * This seed create data for demo section:
 *   3 category, each category contain 10 sections
 *   last section in 3rd category is empty
 *   first section contain 200 topics, all others only one
 *   first topic in first topic contain 100 post, all others only one
 *
 */

const _         = require('lodash');
const co        = require('co');
const thenify   = require('thenify');
const charlatan = require('charlatan');
const ObjectId  = require('mongoose').Types.ObjectId;

let Category;
let Section;
let Topic;
let Post;
let User;
let Vote;
let UserGroup;
let settings;
let parser;

const CATEGORY_COUNT = 3;
const SECTION_COUNT  = 10;
const SUB_SECTION_DEEP = 1;
const TOPIC_COUNT_IN_BIG_SECTION  = 200;
const POST_COUNT_IN_BIG_TOPIC  = 100;
const USER_COUNT = 200;
const MAX_MODERATOR_COUNT = 3;
const MAX_SUB_SECTION_COUNT = 3;
const MAX_VOTES = 10;

let display_order = 0;

function getNextDisplayOrder() {
  display_order++;
  return display_order;
}

let users = [];
let postDay = 0;

const createPost = co.wrap(function* (topic, reply_to) {
  let md = charlatan.Lorem.paragraphs(charlatan.Helpers.rand(5, 1)).join('\n\n');
  let user = users[charlatan.Helpers.rand(USER_COUNT)];

  let options = yield settings.getByCategory('forum_markup', { usergroup_ids: user.usergroups }, { alias: true });

  let result = yield parser({
    text: md,
    attachments: [],
    options
  });

  let ts = new Date(2010, 0, postDay++);

  let post = new Post({
    _id: new ObjectId(Math.round(ts / 1000)),

    html:    result.html,
    md,

    st:      Post.statuses.VISIBLE,
    topic:   topic._id,

    user,

    /*eslint-disable new-cap*/
    ip:      charlatan.Internet.IPv4(),

    /*eslint-disable no-undefined*/
    to:      reply_to ? reply_to._id  : undefined,
    to_user: reply_to ? reply_to.user : undefined,
    to_phid: reply_to ? reply_to.hid  : undefined,

    ts,

    params:  settings
  });

  yield post.save();
  yield User.update({ _id: post.user }, { $inc: { post_count: 1 } });

  return post;
});

const addVotes = co.wrap(function* (post) {
  let votes = 0;

  for (let i = charlatan.Helpers.rand(MAX_VOTES); i > 0; i--) {
    let user = users[charlatan.Helpers.rand(USER_COUNT)];
    let value = Math.random() > 0.5 ? 1 : -1;

    let vote = new Vote({
      to:     post.user,
      from:   user._id,
      'for':  post._id,
      type:   Vote.types.FORUM_POST,
      value
    });

    votes += value;

    yield vote.save();
  }

  yield post.update({ votes });
});

const createTopic = co.wrap(function* (section, post_count) {
  let first_post;
  let last_post;

  let topic = new Topic({
    _id: new ObjectId(Math.round(new Date(2010, 0, postDay) / 1000)),

    title: charlatan.Lorem.sentence().slice(0, -1),

    st: Topic.statuses.OPEN,
    section: section._id,

    views_count: charlatan.Helpers.rand(1000)
  });

  // Save topic to the database before creating posts,
  // it's needed because of Post model hooks
  //
  yield topic.save();

  let posts = [];

  for (let i = 0; i < post_count; i++) {
    // 50% posts won't have any reply information, 25% posts will be
    // answers to the previous post, 12.5% posts will be answers to the
    // 2nd last post and so on.
    //
    let reply_id = posts.length - Math.floor(1 / Math.random()) + 1;
    let reply_to = posts[reply_id];

    var post = yield createPost(topic, reply_to);

    if (!first_post) {
      first_post = post;
    }

    last_post = post;

    posts.push(post);

    yield addVotes(post);
  }

  topic.cache.post_count = post_count;

  topic.cache.first_post = first_post._id;
  topic.cache.first_ts = first_post.ts;
  topic.cache.first_user = first_post.user;

  topic.cache.last_post = last_post._id;
  topic.cache.last_ts = last_post.ts;
  topic.cache.last_user = last_post.user;

  _.assign(topic.cache_hb, topic.cache);

  // Update cache for this topic
  //
  yield topic.save();
});

const createSection = co.wrap(function* (category, sub_section_deep) {
  let section = new Section({
    title: charlatan.Lorem.sentence(charlatan.Helpers.rand(5, 3)).slice(0, -1),
    description: charlatan.Lorem.sentence(),

    parent: category._id,
    display_order: getNextDisplayOrder(),

    cache: {
      real: {}
    }
  });

  yield section.save();

  // add sub-sections
  if (!sub_section_deep || charlatan.Helpers.rand(3) === 2) {
    return;
  }

  for (let i = charlatan.Helpers.rand(MAX_SUB_SECTION_COUNT); i > 0; i--) {
    yield createSection(section, sub_section_deep - 1);
  }
});

const createUsers = co.wrap(function* () {
  let userGroupsByName = {};
  let groups = yield UserGroup.find().select('_id short_name');

  // collect usergroups
  groups.forEach(function (group) {
    userGroupsByName[group.short_name] = group;
  });

  for (let i = 0; i < USER_COUNT; i++) {
    let user = new User({
      first_name: charlatan.Name.firstName(),
      last_name:  charlatan.Name.lastName(),
      nick:       charlatan.Internet.userName(),
      email:      charlatan.Internet.email(),
      joined_ts:  new Date(),
      usergroups: userGroupsByName.members
    });

    yield user.save();

    // add user to store
    users.push(user);
  }
});

let createSections = co.wrap(function* () {
  for (let i = 0; i < CATEGORY_COUNT; i++) {
    let category = new Category({
      title: charlatan.Lorem.sentence(charlatan.Helpers.rand(5, 3)).slice(0, -1),
      description: charlatan.Lorem.sentence(),

      display_order: getNextDisplayOrder('display_order'),
      is_category: true
    });

    yield category.save();

    // create sections
    for (let j = 0; j < SECTION_COUNT; j++) {
      yield createSection(category, SUB_SECTION_DEEP);
    }
  }
});

const updateSectionStat = co.wrap(function* (section) {
  let topicCount;
  let postCount;

  // Clear getSectionTree cache (used in both `updateCache` and `getChildren`
  // functions below).
  //
  Section.getChildren.clear();

  yield Section.updateCache(section._id, true);

  let sections = yield Section.getChildren(section._id, -1);

  // aggregation doesn't return a promise in mongoose?
  let sum = yield thenify(callback => Topic.aggregate(
    { $match: {
      section: { $in: _.map(sections.concat([ section ]), '_id') }
    } },
    { $group: {
      _id: null,
      topic_count: { $sum: 1 },
      post_count: { $sum: '$cache.post_count' }
    } }).exec(callback));

  if (sum && sum[0]) {
    postCount  = sum[0].post_count;
    topicCount = sum[0].topic_count;
  } else {
    // no topics found in section or any of its subsections
    postCount  = 0;
    topicCount = 0;
  }

  section.cache.post_count = postCount;
  section.cache.topic_count = topicCount;

  section.cache_hb.post_count = postCount;
  section.cache_hb.topic_count = topicCount;

  yield section.save();
});

const createTopics = co.wrap(function* () {
  let sections = yield Section.find({ is_category: false })
                              .select('_id cache')
                              .sort({ hid: -1 })
                              .skip(1);

  for (let i = 0; i < sections.length; i++) {
    let section = sections[i];

    // create topic with single post
    yield createTopic(section, 1);
    yield updateSectionStat(section);
  }
});

const fillBigSection = co.wrap(function* () {
  let section = yield Section.findOne({ is_category: false })
                             .sort({ hid: 1 });

  for (let i = 0; i < TOPIC_COUNT_IN_BIG_SECTION; i++) {
    yield createTopic(section, 1);
  }

  yield updateSectionStat(section);
});

const addBigTopic = co.wrap(function* () {
  let section = yield Section.findOne({ is_category: false })
                             .sort({ hid: 1 });

  yield createTopic(section, POST_COUNT_IN_BIG_TOPIC);
  yield updateSectionStat(section);
});

const addModerators = co.wrap(function* () {
  let SectionModeratorStore = settings.getStore('section_moderator');

  if (!SectionModeratorStore) {
    throw new Error('Settings store `section_moderator` is not registered.');
  }

  let sections = yield Section.find({ is_category: false }).select('_id');

  for (let i = 0; i < sections.length; i++) {
    let section = sections[i];

    for (let j = charlatan.Helpers.rand(MAX_MODERATOR_COUNT); j > 0; j--) {
      let user = users[charlatan.Helpers.rand(USER_COUNT)];

      yield SectionModeratorStore.set(
        { forum_mod_visible: { value: true } },
        { section_id: section._id, user_id: user._id }
      );
    }
  }
});

module.exports = co.wrap(function* (N) {
  Category  = N.models.forum.Section;
  Section   = N.models.forum.Section;
  Topic     = N.models.forum.Topic;
  Post      = N.models.forum.Post;
  User      = N.models.users.User;
  UserGroup = N.models.users.UserGroup;
  Vote      = N.models.users.Vote;
  settings  = N.settings;
  parser    = N.parse;

  yield createUsers();
  yield createSections();
  yield createTopics();
  yield fillBigSection();
  yield addBigTopic();
  yield addModerators();
});
<|endoftext|>


<commit_before>/**
 * @file roleUpdate event
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

module.exports = async (oldRole, newRole) => {
  if (oldRole.name === newRole.name) return;

  try {
    let guildSettings = await newRole.client.db.get(`SELECT log FROM guildSettings WHERE guildID=${newRole.guild.id}`);
    if (!guildSettings || !guildSettings.log) return;

    let logChannel = newRole.guild.channels.get(guildSettings.log);
    if (!logChannel) return;

    logChannel.send({
      embed: {
        color: newRole.client.colors.ORANGE,
        title: newRole.guild.client.strings.events(newRole.guild.language, 'roleUpdate'),
        fields: [
          {
            name: 'New Role Name',
            value: newRole.name || '`None`',
            inline: true
          },
          {
            name: 'Old Role Name',
            value: oldRole.name || '`None`',
            inline: true
          },
          {
            name: 'Role ID',
            value: newRole.id,
            inline: true
          }
        ],
        timestamp: new Date()
      }
    }).catch(e => {
      newRole.client.log.error(e);
    });
  }
  catch (e) {
    newRole.client.log.error(e);
  }
};
<commit_msg>Implemented sequelize - roleUpdate event<commit_after>/**
 * @file roleUpdate event
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

module.exports = async (oldRole, newRole) => {
  try {
    if (oldRole.name === newRole.name) return;

    let guildModel = await newRole.client.database.models.guild.findOne({
      attributes: [ 'serverLog' ],
      where: {
        guildID: newRole.guild.id
      }
    });

    if (!guildModel || !guildModel.dataValues.serverLog) return;

    let logChannel = newRole.guild.channels.get(guildModel.dataValues.serverLog);
    if (!logChannel) return;

    logChannel.send({
      embed: {
        color: newRole.client.colors.ORANGE,
        title: newRole.guild.client.strings.events(newRole.guild.language, 'roleUpdate'),
        fields: [
          {
            name: 'New Role Name',
            value: newRole.name || '`None`',
            inline: true
          },
          {
            name: 'Old Role Name',
            value: oldRole.name || '`None`',
            inline: true
          },
          {
            name: 'Role ID',
            value: newRole.id,
            inline: true
          }
        ],
        timestamp: new Date()
      }
    }).catch(e => {
      newRole.client.log.error(e);
    });
  }
  catch (e) {
    newRole.client.log.error(e);
  }
};
<|endoftext|>