<commit_before>(function () {
    'use strict';

    angular
        .module('doleticApp')
        .factory('DepartmentService', DepartmentService);

    DepartmentService.$inject = ['$http', 'SERVER_CONFIG'];

    function DepartmentService($http, SERVER_CONFIG){
        var server = SERVER_CONFIG.url;
        var urlBase = '/api/rh/department';
        var departmentFactory = {departments : {}};

        departmentFactory.getAllDepartments = function (cache) {
            return $http.get(server + urlBase + "s", { cache: cache}).success(function (data) {
                departmentFactory.departments = data.departments;
            }).error(function (data) {
                console.log(data);
            });
        };

        return departmentFactory;
    }
})();<commit_msg>update profile<commit_after>(function () {
    'use strict';

    angular
        .module('doleticApp')
        .factory('DepartmentService', DepartmentService);

    DepartmentService.$inject = ['$http', 'SERVER_CONFIG'];

    function DepartmentService($http, SERVER_CONFIG){
        var server = SERVER_CONFIG.url;
        var urlBase = '/api/rh/departments/enabled';
        var departmentFactory = {departments : {}};

        departmentFactory.getAllDepartments = function (cache) {
            return $http.get(server + urlBase, { cache: cache}).success(function (data) {
                departmentFactory.departments = data.departments;
            }).error(function (data) {
                console.log(data);
            });
        };

        return departmentFactory;
    }
})();<|endoftext|>


<commit_before>function Torrent(torrentData)
{
  this.data = torrentData;
}

Torrent.prototype.size = function()
{
  return (this.data.info.pieces.length * this.data.info["piece length"] / 20);
}
<commit_msg>named basic Torrent object functions<commit_after>function Torrent(torrentData)
{
  this.data = torrentData;
}

Torrent.prototype.size = function()
{
  return (this.data.info.pieces.length * this.data.info["piece length"] / 20);
}

Torrent.prototype.start = function()
{
}

Torrent.prototype.stop = function()
{
}

Torrent.prototype.status = function()
{
}

Torrent.prototype.completionPercentage = function()
{
}
<|endoftext|>


<commit_before>function formatCurrency(amount) {
	var temp = amount.toFixed(2);
	temp.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
	return '$' + temp;
}

function debounce(fn, delay) {
	var timer = null;
	return function () {
		var context = this, args = arguments;
		clearTimeout(timer);
		timer = setTimeout(function () {
			fn.apply(context, args);
		}, delay);
	};
}

function resetRecurringOptions(popupID) {
	$(popupID + ' [name=interval], ' + popupID + ' [name=dayOfMonth], ' + popupID + ' [name=startDate], ' + popupID + ' [name=endDate]').val('');
	$(popupID + ' ul').eq(1).hide();
}

$(document).ready(function() {
	//when receiving the http code that indicates the user is logged out, redirect to the login page
	$(document).ajaxError(function(event, jqxhr, settings, exception) {
		if (jqxhr.status === 401) {
			window.location.href = 'login.php';
		}
	});

	//add close for all actions
	$('.popup').on('click', '.close', function(event) {
		$('.popup').hide();
		event.preventDefault();
	});

	//add
	$('#topControlCenter .controlAdd').click(function(event) {
		$.ajax({
			url: 'ajax.php',
			type: 'POST',
			data: {
				'action': 'add',
				'type': type
			}
		}).done(function(data) {
			$('#defaultPopup > div > div').html(data.html);
			$('#defaultPopup').show();

			$('#addBtn').click(function() {
				var ajaxData = $('#defaultPopup input[type!="checkbox"], #defaultPopup select').serializeArray();
				ajaxData.push(
					{'name': 'action', 'value': 'addSave'},
					{'name': 'type', 'value': type}
				);
				$.ajax({
					url: 'ajax.php',
					type: 'POST',
					data: ajaxData
				}).done(function(data) {
					$('#defaultPopup .invalid').removeClass('invalid');
					if (data.status == 'success') {
						if ('html' in data) {
							$('#defaultPopup > div > div').html(data.html);
						}
						else {
							location.reload();
						}
					}
					else {
						$.each(data, function(key, value) {
							if (key != 'status') {
								$('#defaultPopup [name=' + key + ']').addClass('invalid');
								$('#defaultPopup [name=' + key + ']').qtip({
									'content': value,
									'style': {'classes': 'qtip-tipsy-custom'},
									'position': {
										'my': 'bottom center',
										'at': 'top center'
									},
									show: {'event': 'focus'},
									hide: {'event': 'blur'}
								});
							}
						});
					}
				});
			});
		});
		event.preventDefault();
	});

	//edit
	$('#topControlCenter .controlEdit').click(function(event) {
		if ($(this).hasClass('editEnabled')) {
			var ajaxData;
			var $checked = $('.selectCheckbox:checked');
			if ($checked.length == 0) {
				//item view
				ajaxData = {'action': 'edit', 'type': type, 'id': id};
			}
			else if ($checked.length == 1) {
				//list view, one checked
				ajaxData = {'action': 'edit', 'type': type, 'id': $checked.attr('id')};
			}
			else {
				//list view, more than one checked
				ajaxData = {'action': 'editMany', 'type': type};
			}

			$.ajax({
				url: 'ajax.php',
				type: 'POST',
				data: ajaxData
			}).done(function(data) {
				$('#defaultPopup > div > div').html(data.html);
				$('#defaultPopup').show();
				if (ajaxData.action == 'editMany') {
					$('#defaultPopup input[type="checkbox"]').click(function() {
						$(this).next().next().prop('disabled', !$(this).prop('checked'));
					});
				}

				$('#editBtn').click(function() {
					//if action is editMany, make sure we selected at least one field to change
					if ($checked.length > 1 && $('#defaultPopup input[type="checkbox"]:checked').length == 0) {
						return;
					}

					//get the ids we're changing
					var selectedIDs = [];
					if ($checked.length == 0) {
						selectedIDs.push(id);
					}
					else {
						$.each($checked, function(index, value) {
							selectedIDs.push(value.id);
						});
					}
					var idStr = selectedIDs.join();

					ajaxData = $('#defaultPopup input[type!="checkbox"], #defaultPopup select').serializeArray();
					ajaxData.push(
						{'name': 'action', 'value': 'editSave'},
						{'name': 'type', 'value': type},
						{'name': 'id', 'value': idStr}
					);

					$.ajax({
						url: 'ajax.php',
						type: 'POST',
						data: ajaxData
					}).done(function(data) {
						$('#defaultPopup .invalid').removeClass('invalid');
						if (data.status == 'success') {
							location.reload();
						}
						else {
							$.each(data, function(key, value) {
								if (key != 'status') {
									$('#defaultPopup [name=' + key + ']').addClass('invalid');
									$('#defaultPopup [name=' + key + ']').qtip({
										'content': value,
										'style': {'classes': 'qtip-tipsy-custom'},
										'position': {
											'my': 'bottom center',
											'at': 'top center'
										},
										show: {'event': 'focus'},
										hide: {'event': 'blur'}
									});
								}
							});
						}
					});
				});
			});
		}
		event.preventDefault();
	});

	//delete
	$('#topControlCenter .controlDelete').click(function(event) {
		if ($(this).hasClass('deleteEnabled')) {
			var ajaxData;
			var $checked = $('.selectCheckbox:checked');
			if ($checked.length == 0) {
				//item view
				ajaxData = {'action': 'delete', 'type': type, 'id': id};
			}
			else if ($checked.length == 1) {
				//list view, one checked
				ajaxData = {'action': 'delete', 'type': type, 'id': $checked.attr('id')};
			}
			else {
				//list view, more than one checked
				ajaxData = {'action': 'deleteMany', 'type': type};
			}

			$.ajax({
				url: 'ajax.php',
				type: 'POST',
				data: ajaxData
			}).done(function(data) {
				$('#defaultPopup > div > div').html(data.html);
				$('#defaultPopup').show();

				$('#deleteBtn').click(function() {
					//get the ids we're changing
					var selectedIDs = [];
					if ($checked.length == 0) {
						selectedIDs.push(id);
					}
					else {
						$.each($checked, function(index, value) {
							selectedIDs.push(value.id);
						});
					}
					var idStr = selectedIDs.join();

					$.ajax({
						url: 'ajax.php',
						type: 'POST',
						data: {
							'action': 'deleteSave',
							'type': type,
							'id': idStr
						}
					}).done(function(data) {
						location.reload();
					});
				});
			});
		}
		event.preventDefault();
	});
});<commit_msg>Removed JS formatCurrency function.  Not sure why I added it, it was never used.<commit_after>function debounce(fn, delay) {
	var timer = null;
	return function () {
		var context = this, args = arguments;
		clearTimeout(timer);
		timer = setTimeout(function () {
			fn.apply(context, args);
		}, delay);
	};
}

function resetRecurringOptions(popupID) {
	$(popupID + ' [name=interval], ' + popupID + ' [name=dayOfMonth], ' + popupID + ' [name=startDate], ' + popupID + ' [name=endDate]').val('');
	$(popupID + ' ul').eq(1).hide();
}

$(document).ready(function() {
	//when receiving the http code that indicates the user is logged out, redirect to the login page
	$(document).ajaxError(function(event, jqxhr, settings, exception) {
		if (jqxhr.status === 401) {
			window.location.href = 'login.php';
		}
	});

	//add close for all actions
	$('.popup').on('click', '.close', function(event) {
		$('.popup').hide();
		event.preventDefault();
	});

	//add
	$('#topControlCenter .controlAdd').click(function(event) {
		$.ajax({
			url: 'ajax.php',
			type: 'POST',
			data: {
				'action': 'add',
				'type': type
			}
		}).done(function(data) {
			$('#defaultPopup > div > div').html(data.html);
			$('#defaultPopup').show();

			$('#addBtn').click(function() {
				var ajaxData = $('#defaultPopup input[type!="checkbox"], #defaultPopup select').serializeArray();
				ajaxData.push(
					{'name': 'action', 'value': 'addSave'},
					{'name': 'type', 'value': type}
				);
				$.ajax({
					url: 'ajax.php',
					type: 'POST',
					data: ajaxData
				}).done(function(data) {
					$('#defaultPopup .invalid').removeClass('invalid');
					if (data.status == 'success') {
						if ('html' in data) {
							$('#defaultPopup > div > div').html(data.html);
						}
						else {
							location.reload();
						}
					}
					else {
						$.each(data, function(key, value) {
							if (key != 'status') {
								$('#defaultPopup [name=' + key + ']').addClass('invalid');
								$('#defaultPopup [name=' + key + ']').qtip({
									'content': value,
									'style': {'classes': 'qtip-tipsy-custom'},
									'position': {
										'my': 'bottom center',
										'at': 'top center'
									},
									show: {'event': 'focus'},
									hide: {'event': 'blur'}
								});
							}
						});
					}
				});
			});
		});
		event.preventDefault();
	});

	//edit
	$('#topControlCenter .controlEdit').click(function(event) {
		if ($(this).hasClass('editEnabled')) {
			var ajaxData;
			var $checked = $('.selectCheckbox:checked');
			if ($checked.length == 0) {
				//item view
				ajaxData = {'action': 'edit', 'type': type, 'id': id};
			}
			else if ($checked.length == 1) {
				//list view, one checked
				ajaxData = {'action': 'edit', 'type': type, 'id': $checked.attr('id')};
			}
			else {
				//list view, more than one checked
				ajaxData = {'action': 'editMany', 'type': type};
			}

			$.ajax({
				url: 'ajax.php',
				type: 'POST',
				data: ajaxData
			}).done(function(data) {
				$('#defaultPopup > div > div').html(data.html);
				$('#defaultPopup').show();
				if (ajaxData.action == 'editMany') {
					$('#defaultPopup input[type="checkbox"]').click(function() {
						$(this).next().next().prop('disabled', !$(this).prop('checked'));
					});
				}

				$('#editBtn').click(function() {
					//if action is editMany, make sure we selected at least one field to change
					if ($checked.length > 1 && $('#defaultPopup input[type="checkbox"]:checked').length == 0) {
						return;
					}

					//get the ids we're changing
					var selectedIDs = [];
					if ($checked.length == 0) {
						selectedIDs.push(id);
					}
					else {
						$.each($checked, function(index, value) {
							selectedIDs.push(value.id);
						});
					}
					var idStr = selectedIDs.join();

					ajaxData = $('#defaultPopup input[type!="checkbox"], #defaultPopup select').serializeArray();
					ajaxData.push(
						{'name': 'action', 'value': 'editSave'},
						{'name': 'type', 'value': type},
						{'name': 'id', 'value': idStr}
					);

					$.ajax({
						url: 'ajax.php',
						type: 'POST',
						data: ajaxData
					}).done(function(data) {
						$('#defaultPopup .invalid').removeClass('invalid');
						if (data.status == 'success') {
							location.reload();
						}
						else {
							$.each(data, function(key, value) {
								if (key != 'status') {
									$('#defaultPopup [name=' + key + ']').addClass('invalid');
									$('#defaultPopup [name=' + key + ']').qtip({
										'content': value,
										'style': {'classes': 'qtip-tipsy-custom'},
										'position': {
											'my': 'bottom center',
											'at': 'top center'
										},
										show: {'event': 'focus'},
										hide: {'event': 'blur'}
									});
								}
							});
						}
					});
				});
			});
		}
		event.preventDefault();
	});

	//delete
	$('#topControlCenter .controlDelete').click(function(event) {
		if ($(this).hasClass('deleteEnabled')) {
			var ajaxData;
			var $checked = $('.selectCheckbox:checked');
			if ($checked.length == 0) {
				//item view
				ajaxData = {'action': 'delete', 'type': type, 'id': id};
			}
			else if ($checked.length == 1) {
				//list view, one checked
				ajaxData = {'action': 'delete', 'type': type, 'id': $checked.attr('id')};
			}
			else {
				//list view, more than one checked
				ajaxData = {'action': 'deleteMany', 'type': type};
			}

			$.ajax({
				url: 'ajax.php',
				type: 'POST',
				data: ajaxData
			}).done(function(data) {
				$('#defaultPopup > div > div').html(data.html);
				$('#defaultPopup').show();

				$('#deleteBtn').click(function() {
					//get the ids we're changing
					var selectedIDs = [];
					if ($checked.length == 0) {
						selectedIDs.push(id);
					}
					else {
						$.each($checked, function(index, value) {
							selectedIDs.push(value.id);
						});
					}
					var idStr = selectedIDs.join();

					$.ajax({
						url: 'ajax.php',
						type: 'POST',
						data: {
							'action': 'deleteSave',
							'type': type,
							'id': idStr
						}
					}).done(function(data) {
						location.reload();
					});
				});
			});
		}
		event.preventDefault();
	});
});<|endoftext|>


<commit_before>function RetryOperation(timeouts) {
  this._timeouts = timeouts;
  this._fn = null;
  this._errors = [];
}
module.exports = RetryOperation;

RetryOperation.prototype.retry = function(err) {
  if (!err) {
    return false;
  }

  this._errors.push(err);

  var timeout = this._timeouts.shift();
  if (timeout === undefined) {
    return false;
  }

  setTimeout(function() {
    this._fn();
  }.bind(this), timeout);

  return true;
};

RetryOperation.prototype.try = function(fn) {
  this._fn = fn;
  this._fn();
};

RetryOperation.prototype.errors = function() {
  return this._errors;
};

RetryOperation.prototype.mainError = function() {
  if (this._errors.length === 0) {
    return null;
  }

  var counts = {};
  var mainError = null;
  var mainErrorCount = 0;

  for (var i = 0; i < this._errors.length; i++) {
    var error = this._errors[i];
    var message = error.message;
    var count = (counts[message] || 0) + 1;

    counts[message] = count;

    if (count >= mainErrorCount) {
      mainError = error;
      mainErrorCount = count;
    }
  }

  return mainError;
};
<commit_msg>Simplify setTimeout using bind() in a useful way<commit_after>function RetryOperation(timeouts) {
  this._timeouts = timeouts;
  this._fn = null;
  this._errors = [];
}
module.exports = RetryOperation;

RetryOperation.prototype.retry = function(err) {
  if (!err) {
    return false;
  }

  this._errors.push(err);

  var timeout = this._timeouts.shift();
  if (timeout === undefined) {
    return false;
  }

  setTimeout(this._fn.bind(this), timeout);

  return true;
};

RetryOperation.prototype.try = function(fn) {
  this._fn = fn;
  this._fn();
};

RetryOperation.prototype.errors = function() {
  return this._errors;
};

RetryOperation.prototype.mainError = function() {
  if (this._errors.length === 0) {
    return null;
  }

  var counts = {};
  var mainError = null;
  var mainErrorCount = 0;

  for (var i = 0; i < this._errors.length; i++) {
    var error = this._errors[i];
    var message = error.message;
    var count = (counts[message] || 0) + 1;

    counts[message] = count;

    if (count >= mainErrorCount) {
      mainError = error;
      mainErrorCount = count;
    }
  }

  return mainError;
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name        meteomedia
// @namespace   metmed
// @description Meteomedia done right
// @include     http://www.meteomedia.com/*
// @version     1
// @grant       none
// @require     http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.4.min.js
// ==/UserScript==
function DestroyEverything() {
  $('.headline-area').remove();
  $('.module.coffee-break').remove();
  $('.social-share').remove();
  $('.moon_phases').remove();
  $('#leaderboard').remove();
  $('#sidebar').remove();
  $('#videos_brightcove-multiple-playlist-3-video-city-page').remove();
  $('#map-view').remove();
  $('#gallery').remove();
  $('#news-slider').remove();
  $('#main-footer').remove();
  $('#news').remove();
  $('#videos_brightcove-multiple-playlist-3-video-homepage').remove();
}
$(document).ready(DestroyEverything());
<commit_msg>Removal of the "Commentaire" section that appears when scrolling<commit_after>// ==UserScript==
// @name        meteomedia
// @namespace   metmed
// @description Meteomedia done right
// @author       Gabriel Talbot
// @include     http://www.meteomedia.com/*
// @version     1.07
// @grant       none
// @require     http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.4.min.js
// ==/UserScript==
"use strict";

function ObserveBodyChange(mutations)
{
    $('#feedbackify').remove();
}

function DestroyEverything() {
  $('.headline-area').remove();
  $('.module.coffee-break').remove();
  $('.social-share').remove();
  $('.moon_phases').remove();
  $('.settings').remove();
  $('.signin:parent').remove();

  $('#leaderboard').remove();
  $('#sidebar').remove();
  $('#videos_brightcove-multiple-playlist-3-video-city-page').remove();
  $('#map-view').remove();
  $('#gallery').remove();
  $('#news-slider').remove();
  $('#main-footer').remove();
  $('#news').remove();
  $('#videos_brightcove-multiple-playlist-3-video-homepage').remove();

  $("body").each(function(){
    this.observer = new MutationObserver(ObserveBodyChange);
    var config = { attributes: false, childList: true, characterData: false, subtree: true};
    this.observer.observe(this, config);
  });
}

$(document).ready(DestroyEverything());<|endoftext|>


<commit_before>'use strict';
/**
 * Created by oleksandr at 6/14/14 1:53 PM
 */
angular.module('myApp')
  .directive('dimensionedImg', ['util', '$parse',function(util, $parse) {
    function link($scope, element, attrs) {
      var image = new Image();
      if (!$scope.src) {
        console.log('skip dimensionedImg because src is undefined');
        return;
      }

      console.log($scope.src);
      image.src = $scope.src;
      image.onload = onLoad;

      element.removeAttr('src');
      element.removeAttr('width');
      element.removeAttr('height');

      function onLoad() {
        var dimensions = util.calculateImgDimensions(this, {width: attrs.width, height: attrs.height});
        element.css('width', dimensions.width);
        element.css('height', dimensions.height);
        element.attr('src', $scope.src); // show image when dimensions have been calculated
      }
    }
    return {
      replace: true,
      template: '<img/>',
      scope: {
        src: '=ngSrc',
        width: '=',
        height: '='
      },
      restrict: 'E',
      link: link
    };
  }]);<commit_msg>Remove log<commit_after>'use strict';
/**
 * Created by oleksandr at 6/14/14 1:53 PM
 */
angular.module('myApp')
  .directive('dimensionedImg', ['util', '$parse',function(util, $parse) {
    function link($scope, element, attrs) {
      var image = new Image();
      if (!$scope.src) {
        console.log('skip dimensionedImg because src is undefined');
        return;
      }

      image.src = $scope.src;
      image.onload = onLoad;

      element.removeAttr('src');
      element.removeAttr('width');
      element.removeAttr('height');

      function onLoad() {
        var dimensions = util.calculateImgDimensions(this, {width: attrs.width, height: attrs.height});
        element.css('width', dimensions.width);
        element.css('height', dimensions.height);
        element.attr('src', $scope.src); // show image when dimensions have been calculated
      }
    }
    return {
      replace: true,
      template: '<img/>',
      scope: {
        src: '=ngSrc',
        width: '=',
        height: '='
      },
      restrict: 'E',
      link: link
    };
  }]);<|endoftext|>


<commit_before>'use strict';

const merge = require('webpack-merge');

const webpackConfig = require('./webpack.config.common');

const wpConfig = merge(webpackConfig, {

  entry: {
    'availity-angular': './src/index.js'
  },

  devtool: 'inline-source-map',

  resolve: {
    alias: {
      tester: 'test'
    }
  },

  module: {

    loaders: [
      {
        test: /\.js$/,
        loader: 'babel-istanbul-loader',
        exclude: /(bower_components|node_modules)/
      }
    ]
  },

  debug: false,
  cache: false,
  watch: false

});

module.exports = function(config) {

  config.set({

    // base path used to resolve all patterns
    basePath: 'src',

    frameworks: ['jasmine', 'sinon'],

    files: ['specs.js'],

    reportSlowerThan: 500,

    // if true, Karma runs tests once and exits
    singleRun: true,

    autoWatch: false,

    // files to exclude
    exclude: [
      '*.less',
      '*.css'
    ],

    preprocessors: {
      'specs.js': ['webpack', 'sourcemap']
    },

    webpack: wpConfig,

    webpackMiddleware: {
      stats: {
        hash: false,
        version: false,
        timings: false,
        assets: false,
        chunks: false,
        modules: false,
        reasons: false,
        children: false,
        source: false,
        errors: true,
        errorDetails: true,
        warnings: false,
        publicPath: false
      }
    },

    reporters: ['progress', 'coverage'],

    coverageReporter: {
      reporters: [
        {type: 'lcov', dir: '../coverage/', subdir: '.'},
        {type: 'json', dir: '../coverage/', subdir: '.'},
        {type: 'text-summary'}
      ]
    },

    // web server port
    port: 9876,

    // enable colors in the output
    colors: true,

    client: {
      // log console output in our test console
      captureConsole: true
    },

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    browsers: ['PhantomJS'],

    plugins: [
      require('karma-jasmine'),
      require('karma-coverage'),
      require('karma-sinon'),
      require('karma-notify-reporter'),
      require('karma-spec-reporter'),
      require('karma-phantomjs-launcher'),
      require('karma-webpack')
    ]

  });
};

<commit_msg>update karma coverage config<commit_after>'use strict';

const merge = require('webpack-merge');

const webpackConfig = require('./webpack.config.common');

const wpConfig = merge(webpackConfig, {

  entry: {
    'availity-angular': './src/index.js'
  },

  devtool: 'inline-source-map',

  resolve: {
    alias: {
      tester: 'test'
    }
  },

  module: {

    loaders: [
      {
        test: /\.js$/,
        loader: 'babel-istanbul-loader',
        exclude: /(bower_components|node_modules)/
      }
    ]
  },

  debug: false,
  cache: false,
  watch: false

});

module.exports = function(config) {

  config.set({

    // base path used to resolve all patterns
    basePath: 'src',

    frameworks: ['jasmine', 'sinon'],

    files: ['specs.js'],

    reportSlowerThan: 500,

    // if true, Karma runs tests once and exits
    singleRun: true,

    autoWatch: false,

    // files to exclude
    exclude: [
      '*.less',
      '*.css'
    ],

    preprocessors: {
      'specs.js': ['webpack', 'sourcemap']
    },

    webpack: wpConfig,

    webpackMiddleware: {
      stats: {
        hash: false,
        version: false,
        timings: false,
        assets: false,
        chunks: false,
        modules: false,
        reasons: false,
        children: false,
        source: false,
        errors: true,
        errorDetails: false,
        warnings: false,
        publicPath: false
      }
    },

    reporters: ['progress', 'coverage'],

    coverageReporter: {
      reporters: [
        {type: 'lcov', dir: '../coverage/', subdir: '.'},
        {type: 'json', dir: '../coverage/', subdir: '.'},
        {type: 'text-summary'}
      ]
    },

    // web server port
    port: 9876,

    // enable colors in the output
    colors: true,

    client: {
      // log console output in our test console
      captureConsole: true
    },

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    browsers: ['PhantomJS'],

    plugins: [
      require('karma-jasmine'),
      require('karma-coverage'),
      require('karma-sinon'),
      require('karma-notify-reporter'),
      require('karma-webpack-error-reporter'),
      require('karma-sourcemap-loader'),
      require('karma-spec-reporter'),
      require('karma-phantomjs-launcher'),
      require('karma-webpack')
    ]

  });
};

<|endoftext|>


<commit_before>var when = require( "when" );
var stringify = require( "csv-stringify" );
var LeanKitClient = require( "leankit-client" );
var _ = require( "lodash" );
var options = require( "yargs" )
	.usage( "Usage: $0 -h \"hostname\" -u \"email\" -p \"password\" -b [\"name\" or 1234] [options]" )
	.option( "h", { alias: "host", demand: true, describe: "Host name (e.g. mycompany)", type: "string" } )
	.option( "u", { alias: "user", demand: true, describe: "Account email (e.g. me@company.com)", type: "string" } )
	.option( "p", { alias: "password", demand: true, describe: "Account password", type: "string" } )
	.option( "b", { alias: "board-id", demand: true, describe: "Board name or ID" } )
	.option( "s", { alias: "search", describe: "Search terms", type: "string" } )
	.option( "board", { describe: "Search board", type: "boolean" } )
	.option( "backlog", { describe: "Search backlog", type: "boolean" } )
	.option( "archive", { describe: "Search recent archive", type: "boolean" } )
	.option( "old", { describe: "Search old archive (> 14 days)", type: "boolean" } )
	.option( "comments", { describe: "Search comments", type: "boolean" } )
	.option( "tags", { describe: "Search tags", type: "boolean" } )
	.option( "csv", { alias: "csv", describe: "CSV output", "type": "boolean" } )
	.option( "json", { alias: "json", describe: "JSON output", "type": "boolean" } )
	.help( "?" )
	.alias( "?", "help" )
	.epilog( "Copyright 2015 LeanKit" )
	.argv;

if ( !options.backlog && !options.board && !options.archive && !options.old )
	options.board = true;

if ( !options.csv && !options.json )
	options.csv = true;

var client = LeanKitClient.createClient( options.host, options.user, options.password );

var searchOptions = {
	SearchTerm: options.search || "",
	SearchInBacklog: options.backlog,
	SearchInBoard: options.board,
	SearchInRecentArchive: options.archive,
	SearchInOldArchive: options.old,
	IncludeComments: options.comments,
	IncludeTags: options.tags
};

getBoardId( options.b )
	.then( function( boardId ) {
		return boardId;
	}, function( err ) {
			console.error( "Error getting board by name:", err );
		} )
	.then( function( boardId ) {
		return getCardsBySearch( boardId, searchOptions );
	} )
	.then( function( cards ) {
		if ( options.json ) {
			console.log( JSON.stringify( cards ) );
		} else {
			stringify( cards, { header: true }, function( err, csvOutput ) {
				if ( err ) console.error( "err:", err );
				console.log( csvOutput );
			} );
		}
	}, function( err ) {
			console.error( "Error searching cards:", err );
		} );

function getBoardId( idOrName ) {
	return when.promise( function( resolve, reject ) {
		if ( typeof (idOrName) === "number" ) {
			resolve( idOrName );
		} else {
			client.getBoards( function( err, boards ) {
				if ( err ) {
					return reject( err );
				}

				var board = null;
				for (var i = 0; i < boards.length; i++) {
					if ( boards[ i ].Title === idOrName )
						board = boards[ i ];
				}
				if ( board ) {
					resolve( board.Id );
				} else {
					reject( "Board named [" + idOrName + "] not found" );
				}
			} );
		}
	} );
}

function getCardsBySearch( boardId, searchOptions ) {
	return when.promise( function( resolve, reject ) {
		var cards = [];
		searchOptions.Page = 1;
		searchOptions.MaxResults = 20;
		var counter = 0;
		var maxRecursion = 10;

		var recurse = _.throttle( function( boardId, searchOptions ) {
			counter++;
			client.searchCards( boardId, searchOptions, function( err, res ) {
				if ( err ) return done( err );
				if ( res && res.Results && res.Results.length > 0 ) {
					cards.push.apply( cards, res.Results );

					if ( cards.length >= res.TotalResults || counter >= maxRecursion ) {
						return done();
					} else {
						searchOptions.Page++;
						recurse( boardId, searchOptions );
					}
				} else {
					return done();
				}
			} );
		}, 500 );

		function done( err ) {
			if ( err ) {
				return reject( err );
			}
			return resolve( cards );
		}

		recurse( boardId, searchOptions );

	} );
}
<commit_msg>Updating search sample<commit_after>var when = require( "when" );
var stringify = require( "csv-stringify" );
var LeanKitClient = require( "leankit-client" );
var _ = require( "lodash" );
var options = require( "yargs" )
	.usage( "Usage: $0 -h \"hostname\" -u \"email\" -p \"password\" -b [\"name\" or 1234] [options]" )
	.option( "h", { alias: "host", demand: true, describe: "Host name (e.g. mycompany)", type: "string" } )
	.option( "u", { alias: "user", demand: true, describe: "Account email (e.g. me@company.com)", type: "string" } )
	.option( "p", { alias: "password", demand: true, describe: "Account password", type: "string" } )
	.option( "b", { alias: "board-id", demand: true, describe: "Board name or ID" } )
	.option( "s", { alias: "search", describe: "Search terms", type: "string" } )
	.option( "board", { describe: "Search board", type: "boolean" } )
	.option( "backlog", { describe: "Search backlog", type: "boolean" } )
	.option( "archive", { describe: "Search recent archive", type: "boolean" } )
	.option( "old", { describe: "Search old archive (> 14 days)", type: "boolean" } )
	.option( "comments", { describe: "Search comments", type: "boolean" } )
	.option( "tags", { describe: "Search tags", type: "boolean" } )
	.option( "csv", { alias: "csv", describe: "CSV output", "type": "boolean" } )
	.option( "json", { alias: "json", describe: "JSON output", "type": "boolean" } )
	.help( "?" )
	.alias( "?", "help" )
	.epilog( "Copyright 2015 LeanKit" )
	.argv;

if ( !options.backlog && !options.board && !options.archive && !options.old )
	options.board = true;

if ( !options.csv && !options.json )
	options.csv = true;

var client = LeanKitClient.createClient( options.host, options.user, options.password );

var searchOptions = {
	SearchTerm: options.search || "",
	SearchInBacklog: options.backlog,
	SearchInBoard: options.board,
	SearchInRecentArchive: options.archive,
	SearchInOldArchive: options.old,
	IncludeComments: options.comments,
	IncludeTags: options.tags
};

getBoardId( options.b )
	.then( function( boardId ) {
		return getCardsBySearch( boardId, searchOptions );
	}, function( err ) {
			console.error( "Error getting board by id or name:", err );
		} )
	.then( function( cards ) {
		if ( options.json ) {
			console.log( JSON.stringify( cards ) );
		} else {
			stringify( cards, { header: true }, function( err, csvOutput ) {
				if ( err ) console.error( "err:", err );
				console.log( csvOutput );
			} );
		}
	}, function( err ) {
			console.error( "Error searching cards:", err );
		} );

function getBoardId( idOrName ) {
	return when.promise( function( resolve, reject ) {
		if ( typeof (idOrName) === "number" ) {
			resolve( idOrName );
		} else {
			client.getBoards( function( err, boards ) {
				if ( err ) {
					return reject( err );
				}

				var board = null;
				for (var i = 0; i < boards.length; i++) {
					if ( boards[ i ].Title === idOrName )
						board = boards[ i ];
				}
				if ( board ) {
					resolve( board.Id );
				} else {
					reject( "Board named [" + idOrName + "] not found" );
				}
			} );
		}
	} );
}

function getCardsBySearch( boardId, searchOptions ) {
	return when.promise( function( resolve, reject ) {
		var cards = [];
		searchOptions.Page = 1;
		searchOptions.MaxResults = 20;
		var counter = 0;
		var maxRecursion = 10;

		var recurse = _.throttle( function( boardId, searchOptions ) {
			counter++;
			client.searchCards( boardId, searchOptions, function( err, res ) {
				if ( err ) return done( err );
				if ( res && res.Results && res.Results.length > 0 ) {
					cards.push.apply( cards, res.Results );

					if ( cards.length >= res.TotalResults || counter >= maxRecursion ) {
						return done();
					} else {
						searchOptions.Page++;
						recurse( boardId, searchOptions );
					}
				} else {
					return done();
				}
			} );
		}, 500 );

		function done( err ) {
			if ( err ) {
				return reject( err );
			}
			return resolve( cards );
		}

		recurse( boardId, searchOptions );

	} );
}
<|endoftext|>


<commit_before>const assert = require('assert');

const errors = require('../errors');
const routeGET = require('./routes/routeGET');
const routePUT = require('./routes/routePUT');
const routeDELETE = require('./routes/routeDELETE');
const routeHEAD = require('./routes/routeHEAD');
const routePOST = require('./routes/routePOST');
const routeOPTIONS = require('./routes/routeOPTIONS');
const routesUtils = require('./routesUtils');
const routeWebsite = require('./routes/routeWebsite');

const routeMap = {
    GET: routeGET,
    PUT: routePUT,
    POST: routePOST,
    DELETE: routeDELETE,
    HEAD: routeHEAD,
    OPTIONS: routeOPTIONS,
};

function isValidReqUids(reqUids) {
    // baseline check, to avoid the risk of running into issues if
    // users craft a large x-scal-request-uids header
    return reqUids.length < 128;
}

function checkUnsupportedRoutes(reqMethod) {
    const method = routeMap[reqMethod];
    if (!method) {
        return { error: errors.MethodNotAllowed };
    }
    return { method };
}

function checkBucketAndKey(bucketName, objectKey, method, reqQuery,
    blacklistedPrefixes, log) {
    // if empty name and request not a list Buckets
    if (!bucketName && !(method === 'GET' && !objectKey)) {
        log.debug('empty bucket name', { method: 'routes' });
        return (method !== 'OPTIONS') ?
            errors.MethodNotAllowed : errors.AccessForbidden
               .customizeDescription('CORSResponse: Bucket not found');
    }
    if (bucketName !== undefined && routesUtils.isValidBucketName(bucketName,
        blacklistedPrefixes.bucket) === false) {
        log.debug('invalid bucket name', { bucketName });
        return errors.InvalidBucketName;
    }
    if (objectKey !== undefined) {
        const result = routesUtils.isValidObjectKey(objectKey,
            blacklistedPrefixes.object);
        if (!result.isValid) {
            log.debug('invalid object key', { objectKey });
            return errors.InvalidArgument.customizeDescription('Object key ' +
            `must not start with "${result.invalidPrefix}".`);
        }
    }
    if ((reqQuery.partNumber || reqQuery.uploadId)
        && objectKey === undefined) {
        return errors.InvalidRequest
            .customizeDescription('A key must be specified');
    }
    return undefined;
}

function checkTypes(req, res, params, logger) {
    assert.strictEqual(typeof req, 'object',
        'bad routes param: req must be an object');
    assert.strictEqual(typeof res, 'object',
        'bad routes param: res must be an object');
    assert.strictEqual(typeof logger, 'object',
        'bad routes param: logger must be an object');
    assert.strictEqual(typeof params.api, 'object',
        'bad routes param: api must be an object');
    assert.strictEqual(typeof params.api.callApiMethod, 'function',
        'bad routes param: api.callApiMethod must be a defined function');
    assert.strictEqual(typeof params.internalHandlers, 'object',
        'bad routes param: internalHandlers must be an object');
    if (params.statsClient) {
        assert.strictEqual(typeof params.statsClient, 'object',
        'bad routes param: statsClient must be an object');
    }
    assert(Array.isArray(params.allEndpoints),
        'bad routes param: allEndpoints must be an array');
    assert(params.allEndpoints.length > 0,
        'bad routes param: allEndpoints must have at least one endpoint');
    params.allEndpoints.forEach(endpoint => {
        assert.strictEqual(typeof endpoint, 'string',
        'bad routes param: each item in allEndpoints must be a string');
    });
    assert(Array.isArray(params.websiteEndpoints),
        'bad routes param: allEndpoints must be an array');
    params.websiteEndpoints.forEach(endpoint => {
        assert.strictEqual(typeof endpoint, 'string',
        'bad routes param: each item in websiteEndpoints must be a string');
    });
    assert.strictEqual(typeof params.blacklistedPrefixes, 'object',
        'bad routes param: blacklistedPrefixes must be an object');
    assert(Array.isArray(params.blacklistedPrefixes.bucket),
        'bad routes param: blacklistedPrefixes.bucket must be an array');
    params.blacklistedPrefixes.bucket.forEach(pre => {
        assert.strictEqual(typeof pre, 'string',
        'bad routes param: each blacklisted bucket prefix must be a string');
    });
    assert(Array.isArray(params.blacklistedPrefixes.object),
        'bad routes param: blacklistedPrefixes.object must be an array');
    params.blacklistedPrefixes.object.forEach(pre => {
        assert.strictEqual(typeof pre, 'string',
        'bad routes param: each blacklisted object prefix must be a string');
    });
    assert.strictEqual(typeof params.dataRetrievalFn, 'function',
        'bad routes param: dataRetrievalFn must be a defined function');
}

/** routes - route request to appropriate method
 * @param {Http.Request} req - http request object
 * @param {Http.ServerResponse} res - http response sent to the client
 * @param {object} params - additional routing parameters
 * @param {object} params.api - all api methods and method to call an api method
 *  i.e. api.callApiMethod(methodName, request, response, log, callback)
 * @param {function} params.internalHandlers - internal handlers API object
 *  for queries beginning with '/_/'
 * @param {StatsClient} [params.statsClient] - client to report stats to Redis
 * @param {string[]} params.allEndpoints - all accepted REST endpoints
 * @param {string[]} params.websiteEndpoints - all accepted website endpoints
 * @param {object} params.blacklistedPrefixes - blacklisted prefixes
 * @param {string[]} params.blacklistedPrefixes.bucket - bucket prefixes
 * @param {string[]} params.blacklistedPrefixes.object - object prefixes
 * @param {object} params.unsupportedQueries - object containing true/false
 *  values for whether queries are supported
 * @param {function} params.dataRetrievalFn - function to retrieve data
 * @param {RequestLogger} logger - werelogs logger instance
 * @returns {undefined}
 */
function routes(req, res, params, logger) {
    checkTypes(req, res, params, logger);

    const {
        api,
        internalHandlers,
        statsClient,
        allEndpoints,
        websiteEndpoints,
        blacklistedPrefixes,
        dataRetrievalFn,
    } = params;

    const clientInfo = {
        clientIP: req.socket.remoteAddress,
        clientPort: req.socket.remotePort,
        httpMethod: req.method,
        httpURL: req.url,
        endpoint: req.endpoint,
    };

    let reqUids = req.headers['x-scal-request-uids'];
    if (reqUids !== undefined && !isValidReqUids(reqUids)) {
        // simply ignore invalid id (any user can provide an
        // invalid request ID through a crafted header)
        reqUids = undefined;
    }
    const log = (reqUids !== undefined ?
                 logger.newRequestLoggerFromSerializedUids(reqUids) :
                 logger.newRequestLogger());
    log.info('received request', clientInfo);

    log.end().addDefaultFields(clientInfo);

    if (req.url.startsWith('/_/')) {
        let internalServiceName = req.url.slice(3);
        const serviceDelim = internalServiceName.indexOf('/');
        if (serviceDelim !== -1) {
            internalServiceName = internalServiceName.slice(0, serviceDelim);
        }
        if (internalHandlers[internalServiceName] === undefined) {
            return routesUtils.responseXMLBody(
                errors.InvalidURI, undefined, res, log);
        }
        return internalHandlers[internalServiceName](
            clientInfo.clientIP, req, res, log, statsClient);
    }

    if (statsClient) {
        // report new request for stats
        statsClient.reportNewRequest();
    }

    try {
        const validHosts = allEndpoints.concat(websiteEndpoints);
        routesUtils.normalizeRequest(req, validHosts);
    } catch (err) {
        log.trace('could not normalize request', { error: err.stack });
        return routesUtils.responseXMLBody(
            errors.InvalidURI, undefined, res, log);
    }

    log.addDefaultFields({
        bucketName: req.bucketName,
        objectKey: req.objectKey,
        bytesReceived: req.parsedContentLength || 0,
        bodyLength: parseInt(req.headers['content-length'], 10) || 0,
    });

    const { error, method } = checkUnsupportedRoutes(req.method, req.query);

    if (error) {
        log.trace('error validating route or uri params', { error });
        return routesUtils.responseXMLBody(error, null, res, log);
    }

    const bucketOrKeyError = checkBucketAndKey(req.bucketName, req.objectKey,
        req.method, req.query, blacklistedPrefixes, log);

    if (bucketOrKeyError) {
        log.trace('error with bucket or key value',
        { error: bucketOrKeyError });
        return routesUtils.responseXMLBody(bucketOrKeyError, null, res, log);
    }

    // bucket website request
    if (websiteEndpoints && websiteEndpoints.indexOf(req.parsedHost) > -1) {
        return routeWebsite(req, res, api, log, statsClient, dataRetrievalFn);
    }

    return method(req, res, api, log, statsClient, dataRetrievalFn);
}

module.exports = routes;
<commit_msg>Fix: S3C-905 Quiet health check request logs<commit_after>const assert = require('assert');

const errors = require('../errors');
const routeGET = require('./routes/routeGET');
const routePUT = require('./routes/routePUT');
const routeDELETE = require('./routes/routeDELETE');
const routeHEAD = require('./routes/routeHEAD');
const routePOST = require('./routes/routePOST');
const routeOPTIONS = require('./routes/routeOPTIONS');
const routesUtils = require('./routesUtils');
const routeWebsite = require('./routes/routeWebsite');

const routeMap = {
    GET: routeGET,
    PUT: routePUT,
    POST: routePOST,
    DELETE: routeDELETE,
    HEAD: routeHEAD,
    OPTIONS: routeOPTIONS,
};

function isValidReqUids(reqUids) {
    // baseline check, to avoid the risk of running into issues if
    // users craft a large x-scal-request-uids header
    return reqUids.length < 128;
}

function checkUnsupportedRoutes(reqMethod) {
    const method = routeMap[reqMethod];
    if (!method) {
        return { error: errors.MethodNotAllowed };
    }
    return { method };
}

function checkBucketAndKey(bucketName, objectKey, method, reqQuery,
    blacklistedPrefixes, log) {
    // if empty name and request not a list Buckets
    if (!bucketName && !(method === 'GET' && !objectKey)) {
        log.debug('empty bucket name', { method: 'routes' });
        return (method !== 'OPTIONS') ?
            errors.MethodNotAllowed : errors.AccessForbidden
               .customizeDescription('CORSResponse: Bucket not found');
    }
    if (bucketName !== undefined && routesUtils.isValidBucketName(bucketName,
        blacklistedPrefixes.bucket) === false) {
        log.debug('invalid bucket name', { bucketName });
        return errors.InvalidBucketName;
    }
    if (objectKey !== undefined) {
        const result = routesUtils.isValidObjectKey(objectKey,
            blacklistedPrefixes.object);
        if (!result.isValid) {
            log.debug('invalid object key', { objectKey });
            return errors.InvalidArgument.customizeDescription('Object key ' +
            `must not start with "${result.invalidPrefix}".`);
        }
    }
    if ((reqQuery.partNumber || reqQuery.uploadId)
        && objectKey === undefined) {
        return errors.InvalidRequest
            .customizeDescription('A key must be specified');
    }
    return undefined;
}

function checkTypes(req, res, params, logger) {
    assert.strictEqual(typeof req, 'object',
        'bad routes param: req must be an object');
    assert.strictEqual(typeof res, 'object',
        'bad routes param: res must be an object');
    assert.strictEqual(typeof logger, 'object',
        'bad routes param: logger must be an object');
    assert.strictEqual(typeof params.api, 'object',
        'bad routes param: api must be an object');
    assert.strictEqual(typeof params.api.callApiMethod, 'function',
        'bad routes param: api.callApiMethod must be a defined function');
    assert.strictEqual(typeof params.internalHandlers, 'object',
        'bad routes param: internalHandlers must be an object');
    if (params.statsClient) {
        assert.strictEqual(typeof params.statsClient, 'object',
        'bad routes param: statsClient must be an object');
    }
    assert(Array.isArray(params.allEndpoints),
        'bad routes param: allEndpoints must be an array');
    assert(params.allEndpoints.length > 0,
        'bad routes param: allEndpoints must have at least one endpoint');
    params.allEndpoints.forEach(endpoint => {
        assert.strictEqual(typeof endpoint, 'string',
        'bad routes param: each item in allEndpoints must be a string');
    });
    assert(Array.isArray(params.websiteEndpoints),
        'bad routes param: allEndpoints must be an array');
    params.websiteEndpoints.forEach(endpoint => {
        assert.strictEqual(typeof endpoint, 'string',
        'bad routes param: each item in websiteEndpoints must be a string');
    });
    assert.strictEqual(typeof params.blacklistedPrefixes, 'object',
        'bad routes param: blacklistedPrefixes must be an object');
    assert(Array.isArray(params.blacklistedPrefixes.bucket),
        'bad routes param: blacklistedPrefixes.bucket must be an array');
    params.blacklistedPrefixes.bucket.forEach(pre => {
        assert.strictEqual(typeof pre, 'string',
        'bad routes param: each blacklisted bucket prefix must be a string');
    });
    assert(Array.isArray(params.blacklistedPrefixes.object),
        'bad routes param: blacklistedPrefixes.object must be an array');
    params.blacklistedPrefixes.object.forEach(pre => {
        assert.strictEqual(typeof pre, 'string',
        'bad routes param: each blacklisted object prefix must be a string');
    });
    assert.strictEqual(typeof params.dataRetrievalFn, 'function',
        'bad routes param: dataRetrievalFn must be a defined function');
}

/** routes - route request to appropriate method
 * @param {Http.Request} req - http request object
 * @param {Http.ServerResponse} res - http response sent to the client
 * @param {object} params - additional routing parameters
 * @param {object} params.api - all api methods and method to call an api method
 *  i.e. api.callApiMethod(methodName, request, response, log, callback)
 * @param {function} params.internalHandlers - internal handlers API object
 *  for queries beginning with '/_/'
 * @param {StatsClient} [params.statsClient] - client to report stats to Redis
 * @param {string[]} params.allEndpoints - all accepted REST endpoints
 * @param {string[]} params.websiteEndpoints - all accepted website endpoints
 * @param {object} params.blacklistedPrefixes - blacklisted prefixes
 * @param {string[]} params.blacklistedPrefixes.bucket - bucket prefixes
 * @param {string[]} params.blacklistedPrefixes.object - object prefixes
 * @param {object} params.unsupportedQueries - object containing true/false
 *  values for whether queries are supported
 * @param {function} params.dataRetrievalFn - function to retrieve data
 * @param {RequestLogger} logger - werelogs logger instance
 * @returns {undefined}
 */
function routes(req, res, params, logger) {
    checkTypes(req, res, params, logger);

    const {
        api,
        internalHandlers,
        statsClient,
        allEndpoints,
        websiteEndpoints,
        blacklistedPrefixes,
        dataRetrievalFn,
    } = params;

    const clientInfo = {
        clientIP: req.socket.remoteAddress,
        clientPort: req.socket.remotePort,
        httpMethod: req.method,
        httpURL: req.url,
        endpoint: req.endpoint,
    };

    let reqUids = req.headers['x-scal-request-uids'];
    if (reqUids !== undefined && !isValidReqUids(reqUids)) {
        // simply ignore invalid id (any user can provide an
        // invalid request ID through a crafted header)
        reqUids = undefined;
    }
    const log = (reqUids !== undefined ?
                 logger.newRequestLoggerFromSerializedUids(reqUids) :
                 logger.newRequestLogger());

    if (!req.url.startsWith('/_/healthcheck')) {
        log.info('received request', clientInfo);
    }

    log.end().addDefaultFields(clientInfo);

    if (req.url.startsWith('/_/')) {
        let internalServiceName = req.url.slice(3);
        const serviceDelim = internalServiceName.indexOf('/');
        if (serviceDelim !== -1) {
            internalServiceName = internalServiceName.slice(0, serviceDelim);
        }
        if (internalHandlers[internalServiceName] === undefined) {
            return routesUtils.responseXMLBody(
                errors.InvalidURI, undefined, res, log);
        }
        return internalHandlers[internalServiceName](
            clientInfo.clientIP, req, res, log, statsClient);
    }

    if (statsClient) {
        // report new request for stats
        statsClient.reportNewRequest();
    }

    try {
        const validHosts = allEndpoints.concat(websiteEndpoints);
        routesUtils.normalizeRequest(req, validHosts);
    } catch (err) {
        log.trace('could not normalize request', { error: err.stack });
        return routesUtils.responseXMLBody(
            errors.InvalidURI, undefined, res, log);
    }

    log.addDefaultFields({
        bucketName: req.bucketName,
        objectKey: req.objectKey,
        bytesReceived: req.parsedContentLength || 0,
        bodyLength: parseInt(req.headers['content-length'], 10) || 0,
    });

    const { error, method } = checkUnsupportedRoutes(req.method, req.query);

    if (error) {
        log.trace('error validating route or uri params', { error });
        return routesUtils.responseXMLBody(error, null, res, log);
    }

    const bucketOrKeyError = checkBucketAndKey(req.bucketName, req.objectKey,
        req.method, req.query, blacklistedPrefixes, log);

    if (bucketOrKeyError) {
        log.trace('error with bucket or key value',
        { error: bucketOrKeyError });
        return routesUtils.responseXMLBody(bucketOrKeyError, null, res, log);
    }

    // bucket website request
    if (websiteEndpoints && websiteEndpoints.indexOf(req.parsedHost) > -1) {
        return routeWebsite(req, res, api, log, statsClient, dataRetrievalFn);
    }

    return method(req, res, api, log, statsClient, dataRetrievalFn);
}

module.exports = routes;
<|endoftext|>


<commit_before>var _ = require('lodash');
var utils = require('../utils');

var Price = require('./price');
var Title = require('./title');
var Language = require('./language');
var ProductId = require('./productid');
var WorkId = require('./workid');
var Contributor = require('./contributor');
var Subject = require('./subject');
var Audience = require('./audience');
var SupplyDetail = require('./supplydetail');
var MediaFile = require('./mediafile');
var OtherText = require('./othertext');
var Imprint = require('./imprint');
var Publisher = require('./publisher');

module.exports = {
    tag: 'Product',
    array: true,
    fields: {
        'record': {
            tag: 'RecordReference',
            raw: true
        },
        'notification': {
            tag: 'NotificationType',
            transform: _.partialRight(utils.pad, 2),
            untransform: Number
        },
        'publishingStatus': {
            tag: 'PublishingStatus',
            transform: _.partialRight(utils.pad, 2),
            untransform: Number
        },
        'id': ProductId,
        'workId': WorkId,
        'form': {
            tag: 'ProductForm',
            transform: String
        },
        'epubType': {
            tag: 'EpubType',
            transform: _.partialRight(utils.pad, 3),
            untransform: Number,
        },
        'cityOfPublication': {
            tag: 'CityOfPublication'
        },
        'countryOfPublication': {
            tag: 'CountryOfPublication'
        },
        'title': Title,
        'prices': Price,
        'language': Language,
        'contributors': Contributor,
        'subjects': Subject,
        'audiences': Audience,
        'medias': MediaFile,
        'supply': SupplyDetail,
        'texts': OtherText,
        'imprint': Imprint,
        'publisher': Publisher,
        'numberOfPages': {
            tag: 'NumberOfPages',
            untransform: Number
        },
    }
}
<commit_msg>Add PublicationDate field<commit_after>var _ = require('lodash');
var utils = require('../utils');

var Price = require('./price');
var Title = require('./title');
var Language = require('./language');
var ProductId = require('./productid');
var WorkId = require('./workid');
var Contributor = require('./contributor');
var Subject = require('./subject');
var Audience = require('./audience');
var SupplyDetail = require('./supplydetail');
var MediaFile = require('./mediafile');
var OtherText = require('./othertext');
var Imprint = require('./imprint');
var Publisher = require('./publisher');

module.exports = {
    tag: 'Product',
    array: true,
    fields: {
        'record': {
            tag: 'RecordReference',
            raw: true
        },
        'notification': {
            tag: 'NotificationType',
            transform: _.partialRight(utils.pad, 2),
            untransform: Number
        },
        'publishingStatus': {
            tag: 'PublishingStatus',
            transform: _.partialRight(utils.pad, 2),
            untransform: Number
        },
        'id': ProductId,
        'workId': WorkId,
        'form': {
            tag: 'ProductForm',
            transform: String
        },
        'epubType': {
            tag: 'EpubType',
            transform: _.partialRight(utils.pad, 3),
            untransform: Number,
        },
        'cityOfPublication': {
            tag: 'CityOfPublication'
        },
        'countryOfPublication': {
            tag: 'CountryOfPublication'
        },
        'publicationDate': {
            tag: 'PublicationDate',
            transform: utils.formatDate,
            untransform: utils.unformatDate
        },
        'title': Title,
        'prices': Price,
        'language': Language,
        'contributors': Contributor,
        'subjects': Subject,
        'audiences': Audience,
        'medias': MediaFile,
        'supply': SupplyDetail,
        'texts': OtherText,
        'imprint': Imprint,
        'publisher': Publisher,
        'numberOfPages': {
            tag: 'NumberOfPages',
            untransform: Number
        },
    }
}
<|endoftext|>


<commit_before>(function() {
    'use strict';

    var DIR             = __dirname + '/../../',
        DIR_LIB         = DIR + 'lib/',

        Util            = require(DIR_LIB + 'util'),
        CloudFunc       = require(DIR_LIB + 'cloudfunc'),

        tryRequire      = require('tryrequire'),

        pty             = tryRequire('pty.js'),

        Clients         = [],

        CHANNEL         = CloudFunc.CHANNEL_TERMINAL,
        CHANNEL_RESIZE  = CloudFunc.CHANNEL_TERMINAL_RESIZE,

        ConNum          = 0;

    module.exports = function(socket) {
        Util.check(arguments, ['socket']);

        if (pty)
            socket
                .of('/terminal')
                .on('connection', function(socket) {
                    onConnection(socket, function(channel, data) {
                        socket.emit(channel, data);
                    });
                });
    };

    function onConnection(socket, callback) {
        var onDisconnect, resizeFunc, dataFunc, term;

        ++ConNum;

        if (!Clients[ConNum]) {
            log(ConNum, 'terminal connected');

            term                        = getTerm(callback);
            dataFunc                    = onData.bind(null, term);
            resizeFunc                  = onResize.bind(null, term, callback);

            onDisconnect                = function(conNum, term) {
                Clients[conNum]         = null;

                log(conNum, 'terminal disconnected');

                socket.removeListener(CHANNEL, dataFunc);
                socket.removeListener(CHANNEL_RESIZE, resizeFunc);
                socket.removeListener('disconnect', onDisconnect);

                term.destroy();
            }.bind(null, ConNum, term);

            socket.on(CHANNEL, dataFunc);
            socket.on(CHANNEL_RESIZE, resizeFunc);
            socket.on('disconnect', onDisconnect);
        } else {
            log(ConNum, ' in use. Reconnecting...\n');
            socket.disconnect();
        }
    }

    function onResize(term, callback, size) {
        term.resize(size.cols, size.rows);
        Util.exec(callback, CHANNEL_RESIZE, size);
    }

    function onData(term, data) {
        term.write(data);
    }

    function getTerm(callback) {
        var onData      = Util.exec.bind(Util, callback, CHANNEL),

            term        = pty.spawn('bash', [], {
                name: 'xterm-color',
                cols: 80,
                rows: 25,
                cwd : DIR,
                env : process.env
            });

        term.on('data', onData);

        return term;
    }

    function log(pConnNum, pStr, pType) {
        var lRet,
            lType       = ' ';

        if (pStr) {

            if (pType)
                lType  += pType + ':';

            lRet        = 'client #' + pConnNum + lType + pStr;

            console.log(lRet);
        }

        return lRet;
    }
})();
<commit_msg>chore(terminal) rm " "<commit_after>(function() {
    'use strict';

    var DIR             = __dirname + '/../../',
        DIR_LIB         = DIR + 'lib/',

        Util            = require(DIR_LIB + 'util'),
        CloudFunc       = require(DIR_LIB + 'cloudfunc'),

        tryRequire      = require('tryrequire'),

        pty             = tryRequire('pty.js'),

        Clients         = [],

        CHANNEL         = CloudFunc.CHANNEL_TERMINAL,
        CHANNEL_RESIZE  = CloudFunc.CHANNEL_TERMINAL_RESIZE,

        ConNum          = 0;

    module.exports = function(socket) {
        Util.check(arguments, ['socket']);

        if (pty)
            socket
                .of('/terminal')
                .on('connection', function(socket) {
                    onConnection(socket, function(channel, data) {
                        socket.emit(channel, data);
                    });
                });
    };

    function onConnection(socket, callback) {
        var onDisconnect, resizeFunc, dataFunc, term;

        ++ConNum;

        if (!Clients[ConNum]) {
            log(ConNum, 'terminal connected');

            term                        = getTerm(callback);
            dataFunc                    = onData.bind(null, term);
            resizeFunc                  = onResize.bind(null, term, callback);

            onDisconnect                = function(conNum, term) {
                Clients[conNum]         = null;

                log(conNum, 'terminal disconnected');

                socket.removeListener(CHANNEL, dataFunc);
                socket.removeListener(CHANNEL_RESIZE, resizeFunc);
                socket.removeListener('disconnect', onDisconnect);

                term.destroy();
            }.bind(null, ConNum, term);

            socket.on(CHANNEL, dataFunc);
            socket.on(CHANNEL_RESIZE, resizeFunc);
            socket.on('disconnect', onDisconnect);
        } else {
            log(ConNum, ' in use. Reconnecting...\n');
            socket.disconnect();
        }
    }

    function onResize(term, callback, size) {
        term.resize(size.cols, size.rows);
        Util.exec(callback, CHANNEL_RESIZE, size);
    }

    function onData(term, data) {
        term.write(data);
    }

    function getTerm(callback) {
        var onData      = Util.exec.bind(Util, callback, CHANNEL),

            term        = pty.spawn('bash', [], {
                name: 'xterm-color',
                cols: 80,
                rows: 25,
                cwd : DIR,
                env : process.env
            });

        term.on('data', onData);

        return term;
    }

    function log(pConnNum, pStr, pType) {
        var lRet,
            lType       = ' ';

        if (pStr) {

            if (pType)
                lType  += pType + ':';

            lRet        = 'client #' + pConnNum + lType + pStr;

            console.log(lRet);
        }

        return lRet;
    }
})();
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import moment from 'moment';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import SelectTimeRange, {
  momentRange,
  TIME_RANGE_45_DAYS_AGO
} from '../../components/SelectTimeRange';
import {ALL} from '../../components/SimpleFilterSelect';
import {
  supportsHouse,
  shouldDisplayHouse
} from '../../helpers/PerDistrict';
import SelectDisciplineIncidentType from '../../components/SelectDisciplineIncidentType';
import SelectHouse from '../../components/SelectHouse';
import memoizer from '../../helpers/memoizer';
import FilterBar from '../../components/FilterBar';
import {sortByGrade} from '../../helpers/SortHelpers';
import ExperimentalBanner from '../../components/ExperimentalBanner';
import SectionHeading from '../../components/SectionHeading';
import EscapeListener from '../../components/EscapeListener';
import StudentsTable from '../StudentsTable';
import DashboardBarChart from '../DashboardBarChart';
import * as dashboardStyles from '../dashboardStyles';

export default class SchoolDisciplineDashboard extends React.Component {

  constructor(props) {
    super(props);
    this.state = initialState();
    this.onTimeRangeKeyChanged = this.onTimeRangeKeyChanged.bind(this);
    this.onIncidentTypeChange = this.onIncidentTypeChange.bind(this);
    this.onHouseChanged = this.onHouseChanged.bind(this);
    this.onResetFilters = this.onResetFilters.bind(this);
    this.onColumnClick = this.onColumnClick.bind(this);
    this.onResetStudentList = this.onResetStudentList.bind(this);
    this.onSelectChart = this.onSelectChart.bind(this);
    this.memoize = memoizer();
  }

  filterIncidents(disciplineIncidents, shouldFilterSelectedCategory) {
    return this.memoize(['filteredIncidents', this.state, arguments], () => {
      if (!disciplineIncidents) return [];
      const {nowFn} = this.context;
      const {timeRangeKey, selectedIncidentCode, selectedChart, selectedCategory} = this.state;
      const range = momentRange(timeRangeKey, nowFn());
      return disciplineIncidents.filter(incident => {
        if (!moment.utc(incident.occurred_at).isBetween(range[0], range[1])) return false;
        if (incident.incident_code !== selectedIncidentCode && selectedIncidentCode !== ALL) return false;
        if (shouldFilterSelectedCategory) { //used by the student list when a user selects a category within a chart
          if (selectedChart === 'day' && this.timeStampToDay(incident) !== selectedCategory) return false;
          if (selectedChart === 'time' &&  this.timeStampToHour(incident) !== selectedCategory) return false;
          if (selectedChart === 'incident_location' && incident[selectedChart] !== selectedCategory) return false;
          if (selectedChart === 'incident_code' && incident[selectedChart] !== selectedCategory) return false;
        }
        return true;
      });
    });
  }

  filterStudents(students, shouldFilterSelectedCategory) {
    const {selectedChart, selectedCategory, house} = this.state;
    return students.filter(student => {
      if (house !== ALL && student.house !== house) return false;
      if (selectedCategory && shouldFilterSelectedCategory) { //used by the student list when a user selects a category within a chart
        if (selectedChart === 'grade' && student.grade !== selectedCategory) return false;
        if (selectedChart === 'homeroom_label' && student.homeroom_label !== selectedCategory) return false;
        if (!this.filterIncidents(student.discipline_incidents, true).length) return false;
      }
      if (!this.filterIncidents(student.discipline_incidents, false).length) return false;
      return true;
    });
  }

  timeStampToHour(incident) {
    const hour = moment.utc(incident.occurred_at).startOf('hour').format('h:mm a');
    const timeFormat = "HH:mm a";
    const schoolStart = moment.utc("7:00 am", timeFormat);
    const schoolEnd = moment.utc("3:00 pm", timeFormat);
    if (!incident.has_exact_time) return "Not Logged";
    if (!moment.utc(hour, timeFormat).isBetween(schoolStart, schoolEnd)) return "Other";
    return hour;
  }

  timeStampToDay(incident) {
    return moment.utc(incident.occurred_at).format("ddd");
  }

  getIncidentsFromStudents(students) {
    //for chart data, does not filter on selected categories
    return _.flatten(students.map(student => this.filterIncidents(student.discipline_incidents, false)));
  }

  //Depending on the chart, incidents are grouped either by student attribute or incident attribute. Any future
  //charts may be handled here.
  getChartData(students, selectedChart) {
    switch(selectedChart) {
    case 'homeroom_label': {
      const groupedStudents = _.groupBy(students, selectedChart);
      const categories = this.sortedByIncidentsInStudentGroup(groupedStudents);
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = this.getIncidentsFromStudents(groupedStudents[category]).length;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'grade': {
      const groupedStudents = _.groupBy(students, selectedChart);
      const categories = this.sortedGrades(Object.keys(groupedStudents));
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = this.getIncidentsFromStudents(groupedStudents[category]).length;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'incident_location': {
      const incidents = this.getIncidentsFromStudents(students);
      const groupedIncidents = _.groupBy(incidents, selectedChart);
      const categories = this.sortedByIncidents(groupedIncidents);
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = groupedIncidents[category].length;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'time': {
      const incidents = this.getIncidentsFromStudents(students);
      const groupedIncidents = _.groupBy(incidents, incident => {
        return this.timeStampToHour(incident);
      });
      const categories = this.sortedTimes(Object.keys(groupedIncidents));
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = groupedIncidents[category] ? groupedIncidents[category].length : 0;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'day': {
      const incidents = this.getIncidentsFromStudents(students);
      const groupedIncidents = _.groupBy(incidents, incident => {
        return moment.utc(incident.occurred_at).format("ddd");
      });
      const categories = this.sortedDays();
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = groupedIncidents[category] ? groupedIncidents[category].length : 0;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    }
  }

  sortedDays() {
    return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  }

  sortedTimes(chartKeys) {
    //chartKeys will either contain a time like "4:00 pm", "10:00 am", or "Not Logged"
    return [
      "Not Logged",
      "7:00 am",
      "8:00 am",
      "9:00 am",
      "10:00 am",
      "11:00 am",
      "12:00 pm",
      "1:00 pm",
      "2:00 pm",
      "3:00 pm",
      "Other"];
  }

  sortedGrades(chartKeys) {
    return chartKeys.sort((a,b) => sortByGrade(a,b));
  }

  sortedByIncidents(groupedIncidents) {
    const chartKeys = Object.keys(groupedIncidents);
    return chartKeys.sort((a,b) => {
      return groupedIncidents[b].length - groupedIncidents[a].length;
    });
  }

  sortedByIncidentsInStudentGroup(groupedStudents) {
    return Object.keys(groupedStudents).sort((a,b) => {
      return this.getIncidentsFromStudents(groupedStudents[b]).length - this.getIncidentsFromStudents(groupedStudents[a]).length;
    });
  }

  studentTableRows(students) {
    const filteredStudents = this.filterStudents(students, true);
    const {selectedCategory} = this.state;
    return filteredStudents.map(student => {
      return {
        id: student.id,
        first_name: student.first_name,
        last_name: student.last_name,
        grade: student.grade,
        latest_note: student.latest_note,
        events: this.filterIncidents(student.discipline_incidents, selectedCategory).length
      };
    });
  }

  allIncidentTypes(students) {
    const incidents = students.map(student => {
      return student.discipline_incidents ? student.discipline_incidents : [];
    });
    return _.uniqBy(_.flatten(incidents), 'incident_code')
    .map(incident => incident.incident_code);
  }

  onIncidentTypeChange(incidentType) {
    this.setState({selectedIncidentCode: incidentType, selectedCategory: null});
  }

  onHouseChanged(house) {
    this.setState({house, selectedCategory: null});
  }

  onTimeRangeKeyChanged(timeRangeKey) {
    this.setState({timeRangeKey});
  }

  onSelectChart(selection) {
    this.setState({selectedChart: selection.value, selectedCategory: null});
  }

  onResetFilters() {
    this.setState(initialState());
  }

  onColumnClick(highchartsEvent) {
    this.setState({selectedCategory: highchartsEvent.point.category});
  }

  onResetStudentList() {
    this.setState({selectedCategory: null});
  }

  render() {
    const {districtKey} = this.context;
    const {timeRangeKey, selectedChart, house} = this.state;
    const {school, dashboardStudents} = this.props;
    const chartOptions = [
      {value: 'incident_location', label: 'Location'},
      {value: 'time', label: 'Time'},
      {value: 'homeroom_label', label: 'Classroom'},
      {value: 'grade', label: 'Grade'},
      {value: 'day', label: 'Day'}
    ];
    const incidentTypes = this.allIncidentTypes(dashboardStudents);

    return(
      <EscapeListener className="SchoolDisciplineDashboard" style={styles.flexVertical} onEscape={this.onResetFilters}>
        <ExperimentalBanner />

          <SectionHeading>Discipline incidents at {school.name}</SectionHeading>

            <FilterBar style={styles.timeRange} >
              {supportsHouse(districtKey) && shouldDisplayHouse(school) && (
              <SelectHouse
                house={house}
                onChange={this.onHouseChanged} />
              )}
              <SelectDisciplineIncidentType
                type={this.state.selectedIncidentCode || 'All'}
                onChange={this.onIncidentTypeChange}
                types={incidentTypes}/>
            </FilterBar>
            <FilterBar style={styles.timeRange}>
              <SelectTimeRange
                timeRangeKey={timeRangeKey}
                onChange={this.onTimeRangeKeyChanged} />
            </FilterBar>

              {this.renderStudentDisciplineTable(dashboardStudents)}

                  Break down by:

                <Select
                  value={this.state.selectedChart}
                  onChange={this.onSelectChart}
                  options={chartOptions}
                  style={styles.dropdown}
                  clearable={false}
                />

             {this.renderDisciplineChart(dashboardStudents, selectedChart)}

      </EscapeListener>
    );
  }

  renderDisciplineChart(students, selectedChart) {
    const selectedStudents = this.filterStudents(students, false);
    const {categories, seriesData} = this.getChartData(selectedStudents, selectedChart);
    return (
        <DashboardBarChart
          id = "String"
          animation = {false}
          categories = {{categories: categories}}
          seriesData = {seriesData}
          titleText = {null}
          measureText = {'Number of Incidents'}
          tooltip = {{
            pointFormat: 'Total incidents: {point.y}'}}
          onColumnClick = {this.onColumnClick}
          onBackgroundClick = {this.onResetStudentList}/>
    );
  }

  renderStudentDisciplineTable(students) {
    const rows = this.studentTableRows(students);
    return (
      <StudentsTable
        rows = {rows}
        selectedCategory = {this.state.selectedCategory}
        incidentType={"Incidents"}
        resetFn={this.onResetStudentList}/>
    );
  }
}
SchoolDisciplineDashboard.contextTypes = {
  nowFn: PropTypes.func.isRequired,
  districtKey: PropTypes.string.isRequired
};
SchoolDisciplineDashboard.propTypes = {
  dashboardStudents: PropTypes.array.isRequired,
  school: PropTypes.shape({
    name: PropTypes.string.isRequired
  }).isRequired
};

const styles = {
  flexVertical: {
    flex: 1,
    width: '100%',
    display: 'flex',
    flexDirection: 'column'
  },
  timeRange: {
    display: 'flex',
    justifyContent: 'center'
  },
  graphTitle: {
    display: 'flex',
    justifyContent: 'center',
    marginTop: '20px'
  },
  titleText: {
    fontSize: '18px',
    marginRight: '10px',
    alignSelf: 'center'
  },
  dropdown: {
    width: '200px'
  },
  filterBarContainer: {
    borderBottom: '1px solid #ccc',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    width: '100%',
    padding: 10
  }
};

function initialState() {
  return {
    timeRangeKey: TIME_RANGE_45_DAYS_AGO,
    selectedChart: 'incident_location',
    selectedIncidentCode: ALL,
    selectedCategory: null,
    house: ALL
  };
}
<commit_msg>add grade and counselor<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import moment from 'moment';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import SelectTimeRange, {
  momentRange,
  TIME_RANGE_45_DAYS_AGO
} from '../../components/SelectTimeRange';
import {ALL} from '../../components/SimpleFilterSelect';
import {
  supportsHouse,
  shouldDisplayHouse,
  supportsCounselor,
  shouldDisplayCounselor
} from '../../helpers/PerDistrict';
import SelectDisciplineIncidentType from '../../components/SelectDisciplineIncidentType';
import SelectGrade from '../../components/SelectGrade';
import SelectHouse from '../../components/SelectHouse';
import SelectCounselor from '../../components/SelectCounselor';
import memoizer from '../../helpers/memoizer';
import FilterBar from '../../components/FilterBar';
import {sortByGrade} from '../../helpers/SortHelpers';
import ExperimentalBanner from '../../components/ExperimentalBanner';
import SectionHeading from '../../components/SectionHeading';
import EscapeListener from '../../components/EscapeListener';
import StudentsTable from '../StudentsTable';
import DashboardBarChart from '../DashboardBarChart';
import * as dashboardStyles from '../dashboardStyles';

export default class SchoolDisciplineDashboard extends React.Component {

  constructor(props) {
    super(props);
    this.state = initialState();
    this.onTimeRangeKeyChanged = this.onTimeRangeKeyChanged.bind(this);
    this.onIncidentTypeChange = this.onIncidentTypeChange.bind(this);
    this.onGradeChanged = this.onGradeChanged.bind(this);
    this.onHouseChanged = this.onHouseChanged.bind(this);
    this.onCounselorChanged = this.onCounselorChanged.bind(this);
    this.onResetFilters = this.onResetFilters.bind(this);
    this.onColumnClick = this.onColumnClick.bind(this);
    this.onResetStudentList = this.onResetStudentList.bind(this);
    this.onSelectChart = this.onSelectChart.bind(this);
    this.memoize = memoizer();
  }

  filterIncidents(disciplineIncidents, shouldFilterSelectedCategory) {
    return this.memoize(['filteredIncidents', this.state, arguments], () => {
      if (!disciplineIncidents) return [];
      const {nowFn} = this.context;
      const {timeRangeKey, selectedIncidentCode, selectedChart, selectedCategory} = this.state;
      const range = momentRange(timeRangeKey, nowFn());
      return disciplineIncidents.filter(incident => {
        if (!moment.utc(incident.occurred_at).isBetween(range[0], range[1])) return false;
        if (incident.incident_code !== selectedIncidentCode && selectedIncidentCode !== ALL) return false;
        if (shouldFilterSelectedCategory) { //used by the student list when a user selects a category within a chart
          if (selectedChart === 'day' && this.timeStampToDay(incident) !== selectedCategory) return false;
          if (selectedChart === 'time' &&  this.timeStampToHour(incident) !== selectedCategory) return false;
          if (selectedChart === 'incident_location' && incident[selectedChart] !== selectedCategory) return false;
          if (selectedChart === 'incident_code' && incident[selectedChart] !== selectedCategory) return false;
        }
        return true;
      });
    });
  }

  filterStudents(students, shouldFilterSelectedCategory) {
    const {selectedChart, selectedCategory, grade, house, counselor} = this.state;
    return students.filter(student => {
      if (grade !== ALL && student.grade !== grade) return false;
      if (house !== ALL && student.house !== house) return false;
      if (counselor !== ALL && student.counselor !== counselor) return false;
      if (selectedCategory && shouldFilterSelectedCategory) { //used by the student list when a user selects a category within a chart
        if (selectedChart === 'grade' && student.grade !== selectedCategory) return false;
        if (selectedChart === 'homeroom_label' && student.homeroom_label !== selectedCategory) return false;
        if (!this.filterIncidents(student.discipline_incidents, true).length) return false;
      }
      if (!this.filterIncidents(student.discipline_incidents, false).length) return false;
      return true;
    });
  }

  timeStampToHour(incident) {
    const hour = moment.utc(incident.occurred_at).startOf('hour').format('h:mm a');
    const timeFormat = "HH:mm a";
    const schoolStart = moment.utc("7:00 am", timeFormat);
    const schoolEnd = moment.utc("3:00 pm", timeFormat);
    if (!incident.has_exact_time) return "Not Logged";
    if (!moment.utc(hour, timeFormat).isBetween(schoolStart, schoolEnd)) return "Other";
    return hour;
  }

  timeStampToDay(incident) {
    return moment.utc(incident.occurred_at).format("ddd");
  }

  getIncidentsFromStudents(students) {
    //for chart data, does not filter on selected categories
    return _.flatten(students.map(student => this.filterIncidents(student.discipline_incidents, false)));
  }

  //Depending on the chart, incidents are grouped either by student attribute or incident attribute. Any future
  //charts may be handled here.
  getChartData(students, selectedChart) {
    switch(selectedChart) {
    case 'homeroom_label': {
      const groupedStudents = _.groupBy(students, selectedChart);
      const categories = this.sortedByIncidentsInStudentGroup(groupedStudents);
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = this.getIncidentsFromStudents(groupedStudents[category]).length;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'grade': {
      const groupedStudents = _.groupBy(students, selectedChart);
      const categories = this.sortedGrades(Object.keys(groupedStudents));
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = this.getIncidentsFromStudents(groupedStudents[category]).length;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'incident_location': {
      const incidents = this.getIncidentsFromStudents(students);
      const groupedIncidents = _.groupBy(incidents, selectedChart);
      const categories = this.sortedByIncidents(groupedIncidents);
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = groupedIncidents[category].length;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'time': {
      const incidents = this.getIncidentsFromStudents(students);
      const groupedIncidents = _.groupBy(incidents, incident => {
        return this.timeStampToHour(incident);
      });
      const categories = this.sortedTimes(Object.keys(groupedIncidents));
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = groupedIncidents[category] ? groupedIncidents[category].length : 0;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    case 'day': {
      const incidents = this.getIncidentsFromStudents(students);
      const groupedIncidents = _.groupBy(incidents, incident => {
        return moment.utc(incident.occurred_at).format("ddd");
      });
      const categories = this.sortedDays();
      const seriesData = categories.map(category => {
        const color = (category === this.state.selectedCategory)? 'orange' : null;
        const y = groupedIncidents[category] ? groupedIncidents[category].length : 0;
        return {category, y, color};
      });
      return {categories, seriesData};
    }
    }
  }

  sortedDays() {
    return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  }

  sortedTimes(chartKeys) {
    //chartKeys will either contain a time like "4:00 pm", "10:00 am", or "Not Logged"
    return [
      "Not Logged",
      "7:00 am",
      "8:00 am",
      "9:00 am",
      "10:00 am",
      "11:00 am",
      "12:00 pm",
      "1:00 pm",
      "2:00 pm",
      "3:00 pm",
      "Other"];
  }

  sortedGrades(chartKeys) {
    return chartKeys.sort((a,b) => sortByGrade(a,b));
  }

  sortedByIncidents(groupedIncidents) {
    const chartKeys = Object.keys(groupedIncidents);
    return chartKeys.sort((a,b) => {
      return groupedIncidents[b].length - groupedIncidents[a].length;
    });
  }

  sortedByIncidentsInStudentGroup(groupedStudents) {
    return Object.keys(groupedStudents).sort((a,b) => {
      return this.getIncidentsFromStudents(groupedStudents[b]).length - this.getIncidentsFromStudents(groupedStudents[a]).length;
    });
  }

  studentTableRows(students) {
    const filteredStudents = this.filterStudents(students, true);
    const {selectedCategory} = this.state;
    return filteredStudents.map(student => {
      return {
        id: student.id,
        first_name: student.first_name,
        last_name: student.last_name,
        grade: student.grade,
        latest_note: student.latest_note,
        events: this.filterIncidents(student.discipline_incidents, selectedCategory).length
      };
    });
  }

  allIncidentTypes() {
    const {dashboardStudents} = this.props;
    const incidents = _.uniqBy(_.compact(_.flatten(dashboardStudents.map(student => student.discipline_incidents))), 'incident_code');
    return incidents.map(incident => incident.incident_code).sort();
  }

  allGrades() {
    const {dashboardStudents} = this.props;
    return _.uniq(dashboardStudents.map(student => student.grade)).sort();
  }

  allCounselors() {
    const {dashboardStudents} = this.props;
    return _.uniq(dashboardStudents.map(student => student.counselor)).sort();
  }

  onIncidentTypeChange(incidentType) {
    this.setState({selectedIncidentCode: incidentType, selectedCategory: null});
  }

  onGradeChanged(grade) {
    this.setState({grade, selectedCategory: null});
  }

  onHouseChanged(house) {
    this.setState({house, selectedCategory: null});
  }

  onCounselorChanged(counselor) {
    this.setState({counselor, selectedCategory: null});
  }

  onTimeRangeKeyChanged(timeRangeKey) {
    this.setState({timeRangeKey});
  }

  onSelectChart(selection) {
    this.setState({selectedChart: selection.value, selectedCategory: null});
  }

  onResetFilters() {
    this.setState(initialState());
  }

  onColumnClick(highchartsEvent) {
    this.setState({selectedCategory: highchartsEvent.point.category});
  }

  onResetStudentList() {
    this.setState({selectedCategory: null});
  }

  render() {
    const {districtKey} = this.context;
    const {timeRangeKey, selectedChart, grade, house, counselor} = this.state;
    const {school, dashboardStudents} = this.props;
    const chartOptions = [
      {value: 'incident_location', label: 'Location'},
      {value: 'time', label: 'Time'},
      {value: 'homeroom_label', label: 'Classroom'},
      {value: 'grade', label: 'Grade'},
      {value: 'day', label: 'Day'}
    ];
    const incidentTypes = this.allIncidentTypes(dashboardStudents);

    return(
      <EscapeListener className="SchoolDisciplineDashboard" style={styles.flexVertical} onEscape={this.onResetFilters}>
        <ExperimentalBanner />

          <SectionHeading>Discipline incidents at {school.name}</SectionHeading>

            <FilterBar style={styles.timeRange} >
              <SelectDisciplineIncidentType
                type={this.state.selectedIncidentCode || 'All'}
                onChange={this.onIncidentTypeChange}
                types={incidentTypes} />
              <SelectGrade
                grade={grade}
                grades={this.allGrades()}
                onChange={this.onGradeChanged} />
              {supportsHouse(districtKey) && shouldDisplayHouse(school) && (
                <SelectHouse
                  style={styles.narrowSelect}
                  house={house}
                  onChange={this.onHouseChanged} />
              )}
              {supportsCounselor(districtKey) && shouldDisplayCounselor(school) && (
                <SelectCounselor
                  style={styles.narrowSelect}
                  counselor={counselor}
                  counselors={this.allCounselors()}
                  onChange={this.onCounselorChanged} />
              )}
            </FilterBar>
            <FilterBar style={styles.timeRange}>
              <SelectTimeRange
                timeRangeKey={timeRangeKey}
                onChange={this.onTimeRangeKeyChanged} />
            </FilterBar>

              {this.renderStudentDisciplineTable(dashboardStudents)}

                  Break down by:

                <Select
                  value={this.state.selectedChart}
                  onChange={this.onSelectChart}
                  options={chartOptions}
                  style={styles.dropdown}
                  clearable={false}
                />

             {this.renderDisciplineChart(dashboardStudents, selectedChart)}

      </EscapeListener>
    );
  }

  renderDisciplineChart(students, selectedChart) {
    const selectedStudents = this.filterStudents(students, false);
    const {categories, seriesData} = this.getChartData(selectedStudents, selectedChart);
    return (
        <DashboardBarChart
          id = "String"
          animation = {false}
          categories = {{categories: categories}}
          seriesData = {seriesData}
          titleText = {null}
          measureText = {'Number of Incidents'}
          tooltip = {{
            pointFormat: 'Total incidents: {point.y}'}}
          onColumnClick = {this.onColumnClick}
          onBackgroundClick = {this.onResetStudentList}/>
    );
  }

  renderStudentDisciplineTable(students) {
    const rows = this.studentTableRows(students);
    return (
      <StudentsTable
        rows = {rows}
        selectedCategory = {this.state.selectedCategory}
        incidentType={"Incidents"}
        resetFn={this.onResetStudentList}/>
    );
  }
}
SchoolDisciplineDashboard.contextTypes = {
  nowFn: PropTypes.func.isRequired,
  districtKey: PropTypes.string.isRequired
};
SchoolDisciplineDashboard.propTypes = {
  dashboardStudents: PropTypes.array.isRequired,
  school: PropTypes.shape({
    name: PropTypes.string.isRequired
  }).isRequired
};

const styles = {
  flexVertical: {
    flex: 1,
    width: '100%',
    display: 'flex',
    flexDirection: 'column'
  },
  timeRange: {
    display: 'flex',
    justifyContent: 'center'
  },
  graphTitle: {
    display: 'flex',
    justifyContent: 'center',
    marginTop: '20px'
  },
  titleText: {
    fontSize: '18px',
    marginRight: '10px',
    alignSelf: 'center'
  },
  dropdown: {
    width: '200px'
  },
  filterBarContainer: {
    borderBottom: '1px solid #ccc',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    width: '100%',
    padding: 10
  }
};

function initialState() {
  return {
    timeRangeKey: TIME_RANGE_45_DAYS_AGO,
    selectedChart: 'incident_location',
    selectedIncidentCode: ALL,
    selectedCategory: null,
    grade: ALL,
    house: ALL,
    counselor: ALL
  };
}
<|endoftext|>