<commit_before>const path = require('path')

const archiver = require('archiver')
const extractZip = require('extract-zip')
const fs = require('fs-extra')
const fetch = require('isomorphic-fetch')
const createPushToS3 = require('mastarm/lib/push-to-s3')
const Slack = require('slack')

const {
  collectingCoverage,
  downloadFile,
  getTestFolderFilename,
  isCi,
  isUiRepo,
  killDetachedProcess
} = require('./utils')

const slackConfigured = process.env.SLACK_TOKEN && process.env.SLACK_CHANNEL
const msTeamsConfigured = process.env.MS_TEAMS_WEBHOOK_URL
const logsZipfile = 'logs.zip'
const repo = process.env.TRAVIS_BUILD_DIR
  ? process.env.TRAVIS_BUILD_DIR.split(path.sep).pop()
  : ''
const buildNum = process.env.TRAVIS_BUILD_NUMBER
const uploadedLogsFilename = `${repo}-build-${buildNum}-e2e-logs.zip`
const {LOGS_S3_BUCKET} = process.env

/**
 * Function to kill the coverage server
 */
async function stopCoverageServer () {
  console.log('Killing coverage collector server')
  try {
    await killDetachedProcess('e2e-coverage-collector-server')
  } catch (e) {
    console.error(`An error occurred while trying to stop the coverage server: ${e}`)
    throw e
  }
  console.log('Coverage collector server killed successfully')
}

/**
 * download e2e coverage results and then shut down e2e coverage server
 */
async function collectCoverageAndStopSever () {
  if (!collectingCoverage) return

  // download and save coverage report
  console.log('Downloading coverage report')
  const coverageReportFilename = getTestFolderFilename('e2e-coverage.zip')
  try {
    await downloadFile(
      'http://localhost:9999/coverage/download',
      coverageReportFilename
    )
  } catch (e) {
    console.error(`Error downloading coverage: ${e}`)
    return stopCoverageServer()
  }

  // unzip the coverage report once it's done downloading
  console.log('Extracting coverage report')
  try {
    await promisifiedExtractZip(
      coverageReportFilename,
      {dir: path.join(process.cwd(), 'coverage-e2e')}
    )
  } catch (e) {
    console.error(`error unzipping coverage report: ${e}`)
    return stopCoverageServer()
  }

  await fs.unlink(coverageReportFilename)

  return stopCoverageServer()
}

/**
 * Wrapper to promisify the extractZip module.
 */
function promisifiedExtractZip (filename, options) {
  return new Promise((resolve, reject) => {
    extractZip(filename, options, err => {
      if (err) return reject(err)
      resolve()
    })
  })
}

/**
 * Stub for shutting down a datatools-server instance if needed
 */
function shutdownDatatoolsServer () {
  if (!isCi || !isUiRepo) return Promise.resolve()

  return killDetachedProcess('datatools-server')
}

/**
 * Stub for shutting down a client if needed
 */
function shutdownClient () {
  if (!isCi) return Promise.resolve()

  return killDetachedProcess('client-dev-server')
}

/**
 * Stub for shutting down the OTP server if needed
 */
function shutdownOtp () {
  if (!isCi) return Promise.resolve()

  return killDetachedProcess('otp')
}

function makeUploadFailureHandler (handlerErrorMsg) {
  return (err) => {
    console.error(handlerErrorMsg)
    console.error(`failed to upload logs due to error: ${err}`)
  }
}

/**
 * Upload the logs to s3 if needed and then send a card to MS Teams.
 */
async function uploadToMicrosoftTeams (teamsCallback) {
  if (!msTeamsConfigured) return

  // upload logs to s3 because it was easier to do this than wade through
  // crappy MS Teams documentation about how to link to an office 365 using
  // teams
  let uploadedLogsToS3 = false
  if (LOGS_S3_BUCKET) {
    try {
      await createPushToS3({ s3bucket: LOGS_S3_BUCKET })(
        {
          body: await fs.readFile(logsZipfile),
          outfile: uploadedLogsFilename
        }
      )
      console.log('successfully uploaded to s3')
      uploadedLogsToS3 = true
    } catch (e) {
      console.error('Failed to upload logs to s3: ', e)
    }
  } else {
    console.warn('No S3 bucket defined for the log messages. Logs will not be uploaded anywhere!')
  }

  console.log('posting message to MS Teams')

  const testResults = require(
    path.resolve(`./${getTestFolderFilename('results.json')}`)
  )
  const actions = [{
    '@type': 'OpenUri',
    name: `View Travis Build #${buildNum}`,
    targets: [
      {
        os: 'default',
        uri: process.env.TRAVIS_BUILD_WEB_URL
      }
    ]
  }]

  if (uploadedLogsToS3) {
    actions.push({
      '@type': 'OpenUri',
      name: 'Download Logs',
      targets: [
        {
          os: 'default',
          uri: `https://${LOGS_S3_BUCKET}.s3.amazonaws.com/${uploadedLogsFilename}`
        }
      ]
    })
  }

  let fetchResponse
  try {
    fetchResponse = await fetch(
      process.env.MS_TEAMS_WEBHOOK_URL,
      {
        method: 'POST',
        body: JSON.stringify({
          '@context': 'https://schema.org/extensions',
          '@type': 'MessageCard',
          themeColor: '0072C6',
          title: `${repo} e2e test ${testResults.success ? 'passed. ✅' : 'failed. ❌'}`,
          text: `${testResults.numPassedTests} / ${testResults.numTotalTests} tests passed`,
          potentialAction: actions
        })
      }
    )
  } catch (e) {
    console.error('Failed to post to MS Teams: ', e)
    throw e
  }

  if (fetchResponse.status >= 400) {
    console.error('Received an error code for MS Teams post!')
    throw new Error('Post to MS Teams failed!')
  }

  console.log('successfully posted to MS Teams')
}

/**
 * Upload the zip file containing the lgos to a slack channel.
 */
async function uploadToSlack () {
  if (!slackConfigured) return

  const slack = new Slack({ token: process.env.SLACK_TOKEN })

  console.log('uploading log zipfiles to slack')
  try {
    await slack.files.upload({
      channels: process.env.SLACK_CHANNEL,
      file: fs.createReadStream(logsZipfile),
      filename: uploadedLogsFilename,
      filetype: 'zip',
      initial_comment: `View build logs here: ${process.env.TRAVIS_BUILD_WEB_URL}`
    })
  } catch (e) {
    console.error('failed to upload logs to slack!')
    throw e
  }

  console.log('successfully uploaded logs to slack')
}

/**
 * Find all outputted log files, zip them up and then upload them to a defined
 * slack or MS Teams channel (if defined)
 */
function uploadLogs () {
  if (!(slackConfigured || msTeamsConfigured)) {
    console.warn('Log upload environment variables undefined, not uploading logs anywhere!')
    return
  }

  const output = fs.createWriteStream(logsZipfile)
  const archive = archiver('zip')

  // handle errors if they happen
  archive.on(
    'error',
    makeUploadFailureHandler('An error occurred while archiving')
  )
  output.on(
    'error',
    makeUploadFailureHandler('An error occurred while writing the zip file')
  )

  output.on('close', () => {
    console.log(`Successfully zipped ${archive.pointer()} total bytes`)
    Promise.all([uploadToSlack, uploadToMicrosoftTeams])
      .catch(err => {
        if (err) {
          return makeUploadFailureHandler(
            'An error occurred while uploading the logs'
          )(err)
        }

        console.log('successfully uploaded logs')
      })
  })

  archive.pipe(output)

  // glob all files in test folder and put them in root folder of zip file
  archive.directory(getTestFolderFilename(), false)
  archive.finalize()
}

module.exports = function () {
  // upload the logs after teardown completes because the json results are not
  // written by Jest until after teardown 'completes'
  process.nextTick(() => {
    setTimeout(() => {
      uploadLogs()
    }, 3333)
  })
  return Promise.all([
    collectCoverageAndStopSever(),
    shutdownDatatoolsServer(),
    shutdownClient(),
    shutdownOtp()
  ])
}
<commit_msg>refactor(e2e): execute functions to upload logs<commit_after>const path = require('path')

const archiver = require('archiver')
const extractZip = require('extract-zip')
const fs = require('fs-extra')
const fetch = require('isomorphic-fetch')
const createPushToS3 = require('mastarm/lib/push-to-s3')
const Slack = require('slack')

const {
  collectingCoverage,
  downloadFile,
  getTestFolderFilename,
  isCi,
  isUiRepo,
  killDetachedProcess
} = require('./utils')

const slackConfigured = process.env.SLACK_TOKEN && process.env.SLACK_CHANNEL
const msTeamsConfigured = process.env.MS_TEAMS_WEBHOOK_URL
const logsZipfile = 'logs.zip'
const repo = process.env.TRAVIS_BUILD_DIR
  ? process.env.TRAVIS_BUILD_DIR.split(path.sep).pop()
  : ''
const buildNum = process.env.TRAVIS_BUILD_NUMBER
const uploadedLogsFilename = `${repo}-build-${buildNum}-e2e-logs.zip`
const {LOGS_S3_BUCKET} = process.env

/**
 * Function to kill the coverage server
 */
async function stopCoverageServer () {
  console.log('Killing coverage collector server')
  try {
    await killDetachedProcess('e2e-coverage-collector-server')
  } catch (e) {
    console.error(`An error occurred while trying to stop the coverage server: ${e}`)
    throw e
  }
  console.log('Coverage collector server killed successfully')
}

/**
 * download e2e coverage results and then shut down e2e coverage server
 */
async function collectCoverageAndStopSever () {
  if (!collectingCoverage) return

  // download and save coverage report
  console.log('Downloading coverage report')
  const coverageReportFilename = getTestFolderFilename('e2e-coverage.zip')
  try {
    await downloadFile(
      'http://localhost:9999/coverage/download',
      coverageReportFilename
    )
  } catch (e) {
    console.error(`Error downloading coverage: ${e}`)
    return stopCoverageServer()
  }

  // unzip the coverage report once it's done downloading
  console.log('Extracting coverage report')
  try {
    await promisifiedExtractZip(
      coverageReportFilename,
      {dir: path.join(process.cwd(), 'coverage-e2e')}
    )
  } catch (e) {
    console.error(`error unzipping coverage report: ${e}`)
    return stopCoverageServer()
  }

  await fs.unlink(coverageReportFilename)

  return stopCoverageServer()
}

/**
 * Wrapper to promisify the extractZip module.
 */
function promisifiedExtractZip (filename, options) {
  return new Promise((resolve, reject) => {
    extractZip(filename, options, err => {
      if (err) return reject(err)
      resolve()
    })
  })
}

/**
 * Stub for shutting down a datatools-server instance if needed
 */
function shutdownDatatoolsServer () {
  if (!isCi || !isUiRepo) return Promise.resolve()

  return killDetachedProcess('datatools-server')
}

/**
 * Stub for shutting down a client if needed
 */
function shutdownClient () {
  if (!isCi) return Promise.resolve()

  return killDetachedProcess('client-dev-server')
}

/**
 * Stub for shutting down the OTP server if needed
 */
function shutdownOtp () {
  if (!isCi) return Promise.resolve()

  return killDetachedProcess('otp')
}

function makeUploadFailureHandler (handlerErrorMsg) {
  return (err) => {
    console.error(handlerErrorMsg)
    console.error(`failed to upload logs due to error: ${err}`)
  }
}

/**
 * Upload the logs to s3 if needed and then send a card to MS Teams.
 */
async function uploadToMicrosoftTeams () {
  if (!msTeamsConfigured) return

  // upload logs to s3 because it was easier to do this than wade through
  // crappy MS Teams documentation about how to link to an office 365 using
  // teams
  let uploadedLogsToS3 = false
  if (LOGS_S3_BUCKET) {
    try {
      await createPushToS3({ s3bucket: LOGS_S3_BUCKET })(
        {
          body: await fs.readFile(logsZipfile),
          outfile: uploadedLogsFilename
        }
      )
      console.log('successfully uploaded to s3')
      uploadedLogsToS3 = true
    } catch (e) {
      console.error('Failed to upload logs to s3: ', e)
    }
  } else {
    console.warn('No S3 bucket defined for the log messages. Logs will not be uploaded anywhere!')
  }

  console.log('posting message to MS Teams')

  const testResults = require(
    path.resolve(`./${getTestFolderFilename('results.json')}`)
  )
  const actions = [{
    '@type': 'OpenUri',
    name: `View Travis Build #${buildNum}`,
    targets: [
      {
        os: 'default',
        uri: process.env.TRAVIS_BUILD_WEB_URL
      }
    ]
  }]

  if (uploadedLogsToS3) {
    actions.push({
      '@type': 'OpenUri',
      name: 'Download Logs',
      targets: [
        {
          os: 'default',
          uri: `https://${LOGS_S3_BUCKET}.s3.amazonaws.com/${uploadedLogsFilename}`
        }
      ]
    })
  }

  let fetchResponse
  try {
    fetchResponse = await fetch(
      process.env.MS_TEAMS_WEBHOOK_URL,
      {
        method: 'POST',
        body: JSON.stringify({
          '@context': 'https://schema.org/extensions',
          '@type': 'MessageCard',
          themeColor: '0072C6',
          title: `${repo} e2e test ${testResults.success ? 'passed. ✅' : 'failed. ❌'}`,
          text: `${testResults.numPassedTests} / ${testResults.numTotalTests} tests passed`,
          potentialAction: actions
        })
      }
    )
  } catch (e) {
    console.error('Failed to post to MS Teams: ', e)
    throw e
  }

  if (fetchResponse.status >= 400) {
    console.error('Received an error code for MS Teams post!')
    throw new Error('Post to MS Teams failed!')
  }

  console.log('successfully posted to MS Teams')
}

/**
 * Upload the zip file containing the lgos to a slack channel.
 */
async function uploadToSlack () {
  if (!slackConfigured) return

  const slack = new Slack({ token: process.env.SLACK_TOKEN })

  console.log('uploading log zipfiles to slack')
  try {
    await slack.files.upload({
      channels: process.env.SLACK_CHANNEL,
      file: fs.createReadStream(logsZipfile),
      filename: uploadedLogsFilename,
      filetype: 'zip',
      initial_comment: `View build logs here: ${process.env.TRAVIS_BUILD_WEB_URL}`
    })
  } catch (e) {
    console.error('failed to upload logs to slack!')
    throw e
  }

  console.log('successfully uploaded logs to slack')
}

/**
 * Find all outputted log files, zip them up and then upload them to a defined
 * slack or MS Teams channel (if defined)
 */
function uploadLogs () {
  if (!(slackConfigured || msTeamsConfigured)) {
    console.warn('Log upload environment variables undefined, not uploading logs anywhere!')
    return
  }

  const output = fs.createWriteStream(logsZipfile)
  const archive = archiver('zip')

  // handle errors if they happen
  archive.on(
    'error',
    makeUploadFailureHandler('An error occurred while archiving')
  )
  output.on(
    'error',
    makeUploadFailureHandler('An error occurred while writing the zip file')
  )

  output.on('close', () => {
    console.log(`Successfully zipped ${archive.pointer()} total bytes`)
    Promise.all([uploadToSlack(), uploadToMicrosoftTeams()])
      .then(() => {
        console.log('successfully uploaded logs')
      })
      .catch(err => {
        if (err) {
          return makeUploadFailureHandler(
            'An error occurred while uploading the logs'
          )(err)
        }
      })
  })

  archive.pipe(output)

  // glob all files in test folder and put them in root folder of zip file
  archive.directory(getTestFolderFilename(), false)
  archive.finalize()
}

module.exports = function () {
  // upload the logs after teardown completes because the json results are not
  // written by Jest until after teardown 'completes'
  process.nextTick(() => {
    setTimeout(() => {
      uploadLogs()
    }, 3333)
  })
  return Promise.all([
    collectCoverageAndStopSever(),
    shutdownDatatoolsServer(),
    shutdownClient(),
    shutdownOtp()
  ])
}
<|endoftext|>


<commit_before>
//-------------------------------------------------------
//-- Models

var Issue = Backbone.Model.extend({
	initialize: function(issue) {
		var this_ = this;
		this.set({
			'key': issue['key'],
			'duedate': new Date(issue['fields']['duedate']),
			'estimate': parseInt(issue['fields']['timetracking']['originalEstimateSeconds']),
			'project': {
				'name': issue['fields']['project']['name'],
				'iconUrl': issue['fields']['project']['avatarUrls']['16x16']
			},
			'summary': issue['fields']['summary'],
			'assignee': issue['fields']['assignee'],
			'reporter': issue['fields']['reporter'],
			'issuetype': issue['fields']['issuetype'],
			'priority': issue['fields']['priority'],
			'status': issue['fields']['status'],
			'progress': issue['fields']['progress'],
			'url': issue['self'].replace(/\/rest\/api\/2\/issue\/.*/, '/browse/' + issue['key'])
		});

		chrome.storage.local.get(issue['key'], function(storedAttributes) {
			if (storedAttributes) {
				this_.set(storedAttributes);
			}
		});

		this.on({
			'change:assignee': function(user) {
				app.server.api.updateIssue(this.get('self'), {
					'assignee': user
				}, function(issue) {
					this_.set({
						'assignee': issue['fields']['assignee']
					});
				});
			},
			'change:resolution': function(resolution) {
				app.server.api.updateIssue(this.get('self'), {
					'resolution': resolution
				}, function(issue) {
					this_.set({
						'resolution': issue['fields']['resolution']
					});
				});
			},
			'change:duedate': function(e) {
				app.server.api.updateIssue(this.get('self'), {
					'duedate': moment(e.start).format('YYYY-MM-DD')
				}, function(issue) {
					this_.set({
						'duedate': new Date(issue['fields']['duedate'])
					});
				});
			},
			'change:estiamte': function(e) {
				app.server.api.updateIssue(this.get('self'), {
					'timetracking': {
						'originalEstimateSeconds': (e.end - e.start)/1000
					}
				}, function(issue) {
					this_.set({
						'estimate': parseInt(issue['fields']['timetracking']['originalEstimateSeconds'])
					});
				});
			},
			'change:progress': function(started) {
				if (started) {
					this.set({'started': new Date()});
					var obj = {};
					obj[this.get('key')] = {
						'started': new Date()
					};
					chrome.storage.local.set(obj);
				} else {
					this.unset('started');
					chrome.storage.local.remove(this.get('key'));
				}
			}
		});
	},
	worklog: function(data) {
		app.server.api.worklog(this.get('self'), data);
	},
	comment: function(data) {
		app.server.api.comment(this.get('self'), data);
	}
});

var Issues = Backbone.Collection.extend({
	'model': Issue
});

var Filter = Backbone.Model.extend({
	UPDATE_INTERVAL: 1*60000, // 1min
	timeout: null,
	'issues': new Issues(),
	'initialize': function(filter) {
		var this_ = this;
		this.on('change', function() {
			this.update();
		});
		this.issues.on({
			'change': function() {
				this.trigger('updated', this);
			},
			'reset': function() {
				this.trigger('updated', this);
			}
		});
		this.set({
			'name': filter['name'],
			'jql': filter['jql'],
			'type': filter['type'] || app.FILTER_TYPE_TABLE
		});
		this.update();
		this.collection.trigger('created', this);
		this.timeout = window.setInterval(function() {
			this_.update();
		}, this.UPDATE_INTERVAL);
	},
	'update': function() {
		var this_ = this;
		this.collection.server.api.executeJQL(this.get('jql'), function(issues) {
			this_.issues.reset(issues);
			this_.trigger('updated', this_);
		});
	}
});

var Filters = Backbone.Collection.extend({
    model: Filter
});

var ServerModel = Backbone.Model.extend({
	filters: new Filters,
	api: null,
	sync: function(cmd, server){
		var this_ = this;
		console.log(cmd);
		if (cmd === 'create') {
			this.set('filters', this.filters.toJSON());
			chrome.storage.local.set({
				'server': this_.attributes
			});
		} else if (cmd === 'read') {
			chrome.storage.local.get('server', function(data) {
				this_.set(data['server']);
				this_.trigger('load');
			});
		}
	},
	initialize: function() {
		this.filters.server = this;
		this.on('connected', function() {
			console.log(this.get('filters'));
			this.filters.add(this.get('filters'));
		});

		this.on('load', function() {
			var this_ = this;
			if (this.get('url') && this.get('token')) {
				var api = new JIRA(this.get('url'), this.get('url'));
				api.checkAuthorization(function(res, msg) {
					if (res) {
						this_.api = api;
						this_.trigger('connected');
					} else {
						this_.trigger('disconnected');
						if (msg) {
							this_.trigger('connection-error', msg);
						}
					}
				});
			} else {
				this.trigger('disconnected');
			}
		});

		this.on('login', function(e) {
			var this_ = this;
			var api = new JIRA(e.url);
			api.login(e.username, e.password, function(res, data){
				if (res) {
					this_.set({
						'url': e.url,
						'token': data,
						'username': e.username,
						'filters': this_.get('filters') || app.FILTERS_DEFAULT
					});
					this_.save();
					this_.api = api;
					this_.trigger('connected');
				} else {
					app.server.trigger('login-error', data)
				}
			})
		});

		this.listenTo(this.filters, 'all', function(e) {
			if (e == 'add' || e == 'remove' || e == 'change') {
				this.save();
			}
		});

		this.fetch();
	}
});<commit_msg>saving filters<commit_after>
//-------------------------------------------------------
//-- Models

var Issue = Backbone.Model.extend({
	initialize: function(issue) {
		var this_ = this;
		this.set({
			'key': issue['key'],
			'duedate': new Date(issue['fields']['duedate']),
			'estimate': parseInt(issue['fields']['timetracking']['originalEstimateSeconds']),
			'project': {
				'name': issue['fields']['project']['name'],
				'iconUrl': issue['fields']['project']['avatarUrls']['16x16']
			},
			'summary': issue['fields']['summary'],
			'assignee': issue['fields']['assignee'],
			'reporter': issue['fields']['reporter'],
			'issuetype': issue['fields']['issuetype'],
			'priority': issue['fields']['priority'],
			'status': issue['fields']['status'],
			'progress': issue['fields']['progress'],
			'url': issue['self'].replace(/\/rest\/api\/2\/issue\/.*/, '/browse/' + issue['key'])
		});

		chrome.storage.local.get(issue['key'], function(storedAttributes) {
			if (storedAttributes) {
				this_.set(storedAttributes);
			}
		});

		this.on({
			'change:assignee': function(user) {
				app.server.api.updateIssue(this.get('self'), {
					'assignee': user
				}, function(issue) {
					this_.set({
						'assignee': issue['fields']['assignee']
					});
				});
			},
			'change:resolution': function(resolution) {
				app.server.api.updateIssue(this.get('self'), {
					'resolution': resolution
				}, function(issue) {
					this_.set({
						'resolution': issue['fields']['resolution']
					});
				});
			},
			'change:duedate': function(e) {
				app.server.api.updateIssue(this.get('self'), {
					'duedate': moment(e.start).format('YYYY-MM-DD')
				}, function(issue) {
					this_.set({
						'duedate': new Date(issue['fields']['duedate'])
					});
				});
			},
			'change:estiamte': function(e) {
				app.server.api.updateIssue(this.get('self'), {
					'timetracking': {
						'originalEstimateSeconds': (e.end - e.start)/1000
					}
				}, function(issue) {
					this_.set({
						'estimate': parseInt(issue['fields']['timetracking']['originalEstimateSeconds'])
					});
				});
			},
			'change:progress': function(started) {
				if (started) {
					this.set({'started': new Date()});
					var obj = {};
					obj[this.get('key')] = {
						'started': new Date()
					};
					chrome.storage.local.set(obj);
				} else {
					this.unset('started');
					chrome.storage.local.remove(this.get('key'));
				}
			}
		});
	},
	worklog: function(data) {
		app.server.api.worklog(this.get('self'), data);
	},
	comment: function(data) {
		app.server.api.comment(this.get('self'), data);
	}
});

var Issues = Backbone.Collection.extend({
	'model': Issue
});

var Filter = Backbone.Model.extend({
	UPDATE_INTERVAL: 1*60000, // 1min
	timeout: null,
	'issues': new Issues(),
	'initialize': function(filter) {
		var this_ = this;
		this.on('change', function() {
			this.update();
		});
		this.issues.on({
			'change': function() {
				this.trigger('updated', this);
			},
			'reset': function() {
				this.trigger('updated', this);
			}
		});
		this.set({
			'name': filter['name'],
			'jql': filter['jql'],
			'type': filter['type'] || app.FILTER_TYPE_TABLE
		});
		this.update();
		this.collection.trigger('created', this);
		this.timeout = window.setInterval(function() {
			this_.update();
		}, this.UPDATE_INTERVAL);
	},
	'update': function() {
		var this_ = this;
		this.collection.server.api.executeJQL(this.get('jql'), function(issues) {
			this_.issues.reset(issues);
			this_.trigger('updated', this_);
		});
	}
});

var Filters = Backbone.Collection.extend({
    model: Filter
});

var ServerModel = Backbone.Model.extend({
	filters: new Filters,
	api: null,
	sync: function(cmd, server){
		var this_ = this;
		if (cmd === 'read') {
			chrome.storage.local.get('server', function(data) {
				this_.set(data['server']);
				this_.trigger('load');
			});
		}
	},
	initialize: function() {
		this.filters.server = this;
		this.on('connected', function() {
			console.log(this.get('filters'));
			this.filters.add(this.get('filters'));
		});

		this.on({
			'load': function() {
				var this_ = this;
				if (this.get('url') && this.get('token')) {
					var api = new JIRA(this.get('url'), this.get('url'));
					api.checkAuthorization(function(res, msg) {
						if (res) {
							this_.api = api;
							this_.trigger('connected');
						} else {
							this_.trigger('disconnected');
							if (msg) {
								this_.trigger('connection-error', msg);
							}
						}
					});
				} else {
					this.trigger('disconnected');
				}
			},
			'change': function() {
				chrome.storage.local.set({
					'server': this.toJSON()
				});
			}
		});

		this.on('login', function(e) {
			var this_ = this;
			var api = new JIRA(e.url);
			api.login(e.username, e.password, function(res, data){
				if (res) {
					this_.set({
						'url': e.url,
						'token': data,
						'username': e.username
					});
					if (!this_.get('filters')) {
						this_.set({
							'filters': app.FILTERS_DEFAULT
						});
					}
					this_.api = api;
					this_.trigger('connected');
				} else {
					app.server.trigger('login-error', data)
				}
			})
		});

		this.listenTo(this.filters, 'all', function(e) {
			if (e == 'add' || e == 'remove' || e == 'change') {
				this.set('filters', this.filters.toJSON());
				this.trigger('change');
			}
		});

		this.fetch();
	}
});<|endoftext|>


<commit_before><commit_msg>minor additions<commit_after><|endoftext|>


<commit_before>'use strict';

var express = require('express');
var router = express.Router();
var extend = require('node.extend');
var redis = require("redis");
var client = redis.createClient();
var request = require('request');
var lxc = require('../lxc');

router.get('/start/:name', function(req, res, next){
	lxc.start(req.params.name, function(data){
		console.log('start', arguments);
		if(!data){
			res.json({status: 500, name: req.params.name, message: data});
		}else{
			setTimeout(function() {
				lxc.info(req.params.name, null, function(data){
					var domain = req.query.domain || 'vm42.us';
					domain = req.params.name+'.'+domain;
					client.SADD("hosts", domain, function(){});

					var ip = data.ip + ':5000';
					client.HSET(domain, "ip", ip, redis.print);
					client.HSET(domain, "updated", (new Date).getTime(), redis.print);
					client.hset(domain, "include", "proxy.include");
					res.json({status: 200, info: data});
				 });
			}, 5000);

		}
	});
});

router.get('/live/:template/:name', function(req, res, next){
	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
		console.log('live', arguments);
		return res.json(data);
	});
});

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/clone/:template/:name', function(req, res, next){
	return lxc.clone(req.params.name, req.params.template, function(data){
		console.log('clone', arguments);
		if( data.match(/Created container/) ){
			return res.json({status: 200});
		}else{
			return res.json({status: 500, message: data});
		}
	});
});

router.get('/destroy/:name', function(req, res, next){
	return lxc.destroy(req.params.name, function(data){
		console.log('destroy', arguments);
		if(data){
			return res.json({status: 500, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/info/:name', function(req, res, next){
	return lxc.info(req.params.name, function(data){
		return res.json(data);
	});
});

router.get('/list', function(req, res, next) {
	return lxc.list(function(data){
		return res.json(data);
	});
});

router.post('/run/:ip?', function(req, res, next){

	var runner = function(res, req, ip){
		console.log('runner on', ip,'with body:\n', JSON.stringify(req.body));
		return request.post({url:'http://'+ip, form: req.body}, function(error, response, body){
			console.log('request args:', arguments)
			body = JSON.parse(body);
			body['ip'] = ip.replace('10.0.', '')
			return res.json(body)
		});
	};

	if(req.params.ip){
		var ip = '10.0.'+ req.params.ip;
		return runner(res, req, ip);
	}else{
		var name = 'u1-'+(Math.random()*100).toString().replace('.','');
		console.log('new VM', name);
		return lxc.startEphemeral(name, 'u1', function(data){
			return runner(res, req, data.ip);
		});
	}

});

module.exports = router;
<commit_msg>runner<commit_after>'use strict';

var express = require('express');
var router = express.Router();
var extend = require('node.extend');
var redis = require("redis");
var client = redis.createClient();
var request = require('request');
var lxc = require('../lxc');

var runner = function(res, req, ip){
	return request.post({url:'http://'+ip, form: req.body}, function(error, response, body){
		body = JSON.parse(body);
		body['ip'] = ip.replace('10.0.', '')
		return res.json(body)
	});
};
var addToRedis = function(){
	lxc.info(req.params.name, null, function(data){
			var domain = req.query.domain || 'vm42.us';
			domain = req.params.name+'.'+domain;
			client.SADD("hosts", domain, function(){});

			var ip = data.ip + ':5000';
			client.HSET(domain, "ip", ip, redis.print);
			client.HSET(domain, "updated", (new Date).getTime(), redis.print);
			client.hset(domain, "include", "proxy.include");
			return res.json({status: 200, info: data});
	 });
};

router.get('/start/:name', function(req, res, next){
	return lxc.start(req.params.name, function(data){
		console.log('start', arguments);
		if(!data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			res.json({})
		}
	});
});

router.get('/live/:template/:name', function(req, res, next){
	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
		console.log('live', arguments);
		return res.json(data);
	});
});

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/clone/:template/:name', function(req, res, next){
	return lxc.clone(req.params.name, req.params.template, function(data){
		console.log('clone', arguments);
		if( data.match(/Created container/) ){
			return res.json({status: 200});
		}else{
			return res.json({status: 500, message: data});
		}
	});
});

router.get('/destroy/:name', function(req, res, next){
	return lxc.destroy(req.params.name, function(data){
		console.log('destroy', arguments);
		if(data){
			return res.json({status: 500, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/info/:name', function(req, res, next){
	return lxc.info(req.params.name, function(data){
		return res.json(data);
	});
});

router.get('/list', function(req, res, next) {
	return lxc.list(function(data){
		return res.json(data);
	});
});

router.post('/run/:ip?', function(req, res, next){
	if(req.params.ip){
		var ip = '10.0.'+ req.params.ip;
		return runner(res, req, ip);
	}else{
		var name = 'u1-'+(Math.random()*100).toString().replace('.','');
		return lxc.startEphemeral(name, 'u1', function(data){
			return runner(res, req, data.ip);
		});
	}
});

module.exports = router;
<|endoftext|>


<commit_before>'use strict';

angular.module('app')
.config(function($stateProvider, $urlRouterProvider) {
	$urlRouterProvider.otherwise('/');
	$stateProvider
		.state('index', {
	url: '/',
	templateUrl: 'partials/events.html',
	controller: 'MainCtrl'
		})
		.state('checkin', {
	url: '/checkin/:id',
	templateUrl: 'partials/checkin.html',
	controller: 'EventCtrl'
		});
});
<commit_msg>Fixing indentation on routes file<commit_after>'use strict';

angular.module('app')
.config(function($stateProvider, $urlRouterProvider) {
	$urlRouterProvider.otherwise('/');
	$stateProvider
		.state('index', {
			url: '/',
			templateUrl: 'partials/events.html',
			controller: 'MainCtrl'
		})
		.state('checkin', {
			url: '/checkin/:id',
			templateUrl: 'partials/checkin.html',
			controller: 'EventCtrl'
		});
});
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

var React = require("react");

module.exports = React.createClass({
    displayName: 'TextInputDialog',
    propTypes: {
        title: React.PropTypes.string,
        description: React.PropTypes.oneOfType([
            React.PropTypes.element,
            React.PropTypes.string,
        ]),
        value: React.PropTypes.string,
        button: React.PropTypes.string,
        focus: React.PropTypes.bool,
        onFinished: React.PropTypes.func.isRequired
    },

    getDefaultProps: function() {
        return {
            title: "",
            value: "",
            description: "",
            button: "OK",
            focus: true
        };
    },

    componentDidMount: function() {
        if (this.props.focus) {
            // Set the cursor at the end of the text input
            this.refs.textinput.value = this.props.value;
        }
    },

    onOk: function() {
        this.props.onFinished(true, this.refs.textinput.value);
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    onKeyDown: function(e) {
        if (e.keyCode === 27) { // escape
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(false);
        }
        else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(true, this.refs.textinput.value);
        }
    },

    render: function() {
        return (

                    {this.props.title}

                         {this.props.description}

                        <input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" onKeyDown={this.onKeyDown}/>

                    <button onClick={this.onOk}>
                        {this.props.button}
                    </button>

                    <button onClick={this.onCancel}>
                        Cancel
                    </button>

        );
    }
});
<commit_msg>fix up trailing whitespace and put the buttons in the right order<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

var React = require("react");

module.exports = React.createClass({
    displayName: 'TextInputDialog',
    propTypes: {
        title: React.PropTypes.string,
        description: React.PropTypes.oneOfType([
            React.PropTypes.element,
            React.PropTypes.string,
        ]),
        value: React.PropTypes.string,
        button: React.PropTypes.string,
        focus: React.PropTypes.bool,
        onFinished: React.PropTypes.func.isRequired
    },

    getDefaultProps: function() {
        return {
            title: "",
            value: "",
            description: "",
            button: "OK",
            focus: true
        };
    },

    componentDidMount: function() {
        if (this.props.focus) {
            // Set the cursor at the end of the text input
            this.refs.textinput.value = this.props.value;
        }
    },

    onOk: function() {
        this.props.onFinished(true, this.refs.textinput.value);
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    onKeyDown: function(e) {
        if (e.keyCode === 27) { // escape
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(false);
        }
        else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(true, this.refs.textinput.value);
        }
    },

    render: function() {
        return (

                    {this.props.title}

                         {this.props.description}

                        <input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" onKeyDown={this.onKeyDown}/>

                    <button onClick={this.onCancel}>
                        Cancel
                    </button>
                    <button onClick={this.onOk}>
                        {this.props.button}
                    </button>

        );
    }
});
<|endoftext|>


<commit_before><commit_msg>Removed points<commit_after><|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var path = require('path');

/* GET apk files. */
router.get('/:apk', function(req, res, next) {
  var appRoot =(path.dirname(require.main.filename || process.mainModule.filename));
  var filename = path.resolve(appRoot, '/../public/files/') + req.params.apk;

  fs.stat(filename, (err)=>{
    var readStream = fs.createReadStream(filename);

    readStream.on('open', function() {
      readStream.pipe(res);
    });

    readStream.on('error', function(err) {
      res.end(err);
    });
  });
});

module.exports = router;
<commit_msg>missing declaration of fs on apk end point<commit_after>var express = require('express');
var router = express.Router();
var path = require('path');
var fs = require('fs');

/* GET apk files. */
router.get('/:apk', function(req, res, next) {
  var appRoot =(path.dirname(require.main.filename || process.mainModule.filename));
  var filename = path.resolve(appRoot, '/../public/files/') + req.params.apk;

  fs.stat(filename, (err)=>{
    var readStream = fs.createReadStream(filename);

    readStream.on('open', function() {
      readStream.pipe(res);
    });

    readStream.on('error', function(err) {
      res.end(err);
    });
  });
});

module.exports = router;
<|endoftext|>


<commit_before>'use strict'

import React                        from 'react'
import { Router, Route, IndexRoute, browserHistory } from 'react-router'

import App                  from './App'
import HomeScreenPage        from './HomeScreenPage'

import ProfilesApp          from './profiles/ProfilesApp'
import AllProfilesPage      from './profiles/AllProfilesPage'
import ViewProfilePage      from './profiles/ViewProfilePage'
import EditProfilePage      from './profiles/EditProfilePage'
import RegisterProfilePage  from './profiles/RegisterProfilePage'
import ImportProfilePage    from './profiles/ImportProfilePage'
import ExportProfilePage    from './profiles/ExportProfilePage'
import SearchProfilesPage   from './profiles/SearchProfilesPage'

import AccountApp           from './account/AccountApp'
import DeleteAccountPage    from './account/DeleteAccountPage'
import BackupAccountPage    from './account/BackupAccountPage'
import ChangePasswordPage   from './account/ChangePasswordPage'
import CreateAccountPage    from './account/CreateAccountPage'
import RestoreAccountPage   from './account/RestoreAccountPage'
import ApiSettingsPage      from './account/ApiSettingsPage'
import StorageProvidersPage from './account/StorageProvidersPage'

import WalletApp            from './wallet/WalletApp'
import ReceivePage          from './wallet/ReceivePage'
import SendPage             from './wallet/SendPage'

import AuthPage             from './auth/AuthPage'

import NotFoundPage         from './errors/NotFoundPage'

export default (
<Router history={browserHistory}>
  <Route path="/" component={App}>
    <IndexRoute component={HomeScreenPage} />

    <Route path="profiles" component={ProfilesApp}>
        <IndexRoute component={AllProfilesPage} />
        <Route path="i/search/:query" component={SearchProfilesPage} />
        <Route path=":name"           component={ViewProfilePage} />
        <Route path=":index/local"    component={ViewProfilePage} />
        <Route path=":index/edit"     component={EditProfilePage} />
        <Route path=":index/export"   component={ExportProfilePage} />
        <Route path="i/register/:index" component={RegisterProfilePage} />
        <Route path="i/import"        component={ImportProfilePage} />
    </Route>

    <Route path="account" component={AccountApp}>
        <Route path="delete"      component={DeleteAccountPage} />
        <Route path="backup"      component={BackupAccountPage} />
        <Route path="password"    component={ChangePasswordPage} />
        <Route path="create"      component={CreateAccountPage} />
        <Route path="restore"     component={RestoreAccountPage} />
        <Route path="api"         component={ApiSettingsPage} />
        <Route path="storage"   component={StorageProvidersPage} />
    </Route>

    <Route path="wallet" component={WalletApp}>
        <Route path="receive"     component={ReceivePage} />
        <Route path="send"        component={SendPage} />
    </Route>

    <Route path="/auth" component={AuthPage} />
    <Route path="/*" component={NotFoundPage} />
  </Route>
</Router>
)
<commit_msg>clean lint<commit_after>import React                        from 'react'
import { Router, Route, IndexRoute, browserHistory } from 'react-router'

import App                  from './App'
import HomeScreenPage        from './HomeScreenPage'

import ProfilesApp          from './profiles/ProfilesApp'
import AllProfilesPage      from './profiles/AllProfilesPage'
import ViewProfilePage      from './profiles/ViewProfilePage'
import EditProfilePage      from './profiles/EditProfilePage'
import RegisterProfilePage  from './profiles/RegisterProfilePage'
import ImportProfilePage    from './profiles/ImportProfilePage'
import ExportProfilePage    from './profiles/ExportProfilePage'
import SearchProfilesPage   from './profiles/SearchProfilesPage'

import AccountApp           from './account/AccountApp'
import DeleteAccountPage    from './account/DeleteAccountPage'
import BackupAccountPage    from './account/BackupAccountPage'
import ChangePasswordPage   from './account/ChangePasswordPage'
import CreateAccountPage    from './account/CreateAccountPage'
import RestoreAccountPage   from './account/RestoreAccountPage'
import ApiSettingsPage      from './account/ApiSettingsPage'
import StorageProvidersPage from './account/StorageProvidersPage'

import WalletApp            from './wallet/WalletApp'
import ReceivePage          from './wallet/ReceivePage'
import SendPage             from './wallet/SendPage'

import AuthPage             from './auth/AuthPage'

import NotFoundPage         from './errors/NotFoundPage'

export default (
  <Router history={browserHistory}>
    <Route path="/" component={App}>
      <IndexRoute component={HomeScreenPage} />

      <Route path="profiles" component={ProfilesApp}>
        <IndexRoute component={AllProfilesPage} />
        <Route path="i/search/:query" component={SearchProfilesPage} />
        <Route path=":name"           component={ViewProfilePage} />
        <Route path=":index/local"    component={ViewProfilePage} />
        <Route path=":index/edit"     component={EditProfilePage} />
        <Route path=":index/export"   component={ExportProfilePage} />
        <Route path="i/register/:index" component={RegisterProfilePage} />
        <Route path="i/import"        component={ImportProfilePage} />
      </Route>

      <Route path="account" component={AccountApp}>
        <Route path="delete"      component={DeleteAccountPage} />
        <Route path="backup"      component={BackupAccountPage} />
        <Route path="password"    component={ChangePasswordPage} />
        <Route path="create"      component={CreateAccountPage} />
        <Route path="restore"     component={RestoreAccountPage} />
        <Route path="api"         component={ApiSettingsPage} />
        <Route path="storage"   component={StorageProvidersPage} />
      </Route>

      <Route path="wallet" component={WalletApp}>
        <Route path="receive"     component={ReceivePage} />
        <Route path="send"        component={SendPage} />
      </Route>

      <Route path="/auth" component={AuthPage} />
      <Route path="/*" component={NotFoundPage} />
    </Route>
  </Router>
)
<|endoftext|>


<commit_before>module.exports = function (app) {

    var Util = require("../util");
    var skinChanger = require("../generator/skinChanger");
    var auth = require("../generator/authentication");
    var dataFetcher = require("../generator/dataFetcher")
    var config = require("../config");

    // Schemas
    var Account = require("../db/schemas/account").Account;
    var Skin = require("../db/schemas/skin").Skin;
    var Traffic = require("../db/schemas/traffic").Traffic;
    var Stat = require("../db/schemas/stat").Stat;

    app.get("/get/delay", function (req, res) {
        var ip = req.realAddress;
        Util.getGeneratorDelay().then(function (delay) {
            Traffic.findOne({ip: ip}).lean().exec(function (err, traffic) {
                if (err) return console.log(err);
                if (traffic) {
                    res.json({delay: delay, next: (traffic.lastRequest.getTime() / 1000) + delay, nextRelative: ((traffic.lastRequest.getTime() / 1000) + delay) - (Date.now() / 1000)});
                } else {
                    res.json({delay: delay, next: Date.now() / 1000, nextRelative: 0});
                }
            })
        })
    })

    app.get("/get/stats/:details?", function (req, res) {
        var stats = {};

        Util.getGeneratorDelay().then(function (delay) {
            stats.delay = delay;

            stats.queues = {
                auth:{
                    delay: config.requestQueue.auth,
                    size: auth.requestQueue.length
                },
                skinChanger: {
                    delay: config.requestQueue.skinChanger,
                    size: skinChanger.requestQueue.length
                }
            };
            stats.cache = {
               dataFetcher: Object.keys(dataFetcher.cache).length
            };

            // Skin.find({}, "duplicate views visibility time name type via generateDuration", function (err, skins) {
            //     if (err) return console.log(err);
            //     var skinStats = buildSkinStats(skins);
            //     Object.assign(stats, skinStats);
            //
            //     Account.count({enabled: true}, function (err, count) {
            //         if (err) return console.log(err);
            //         stats.accounts = count;
            //
            //         Stat.find({}, function (err, s) {
            //             var generateSuccess = 0;
            //             var generateFail = 0;
            //             s.forEach(function (stat) {
            //                 if (stat.key === "generate.success") generateSuccess = stat.value;
            //                 if (stat.key === "generate.fail") generateFail = stat.value;
            //             })
            //
            //             var generateTotal = generateSuccess + generateFail;
            //             stats.successRate = Number((generateSuccess / generateTotal).toFixed(3));
            //
            //             res.json(stats);
            //         })
            //     })
            // })

            Account.count({enabled: true}, function (err, count) {
                if (err) return console.log(err);
                stats.accounts = count;
                Account.count({enabled: true, errorCounter: {$lt: (config.errorThreshold || 10)}}, function (err, healthyCount) {
                    if (err) return console.log(err);
                    stats.healthyAccounts = healthyCount;
                    var time = Date.now()/1000;
                    Account.count({enabled: true, requestServer: {$in: [null, "default", config.server]}, lastUsed: {'$lt': (time - 100)}, forcedTimeoutAt: {'$lt': (time - 500)}, errorCounter: {'$lt': (config.errorThreshold||10)}},function(err,useableCount){
                        if (err) return console.log(err);
                        stats.useableAccounts = useableCount;
                        Stat.find({$or: [{key: "generate.success"}, {key: "generate.fail"}]}).lean().exec(function (err, s) {
                            if (err) return console.log(err);
                            var generateSuccess = 0;
                            var generateFail = 0;
                            s.forEach(function (stat) {
                                if (stat.key === "generate.success") generateSuccess = stat.value;
                                if (stat.key === "generate.fail") generateFail = stat.value;
                            })
                            var generateTotal = generateSuccess + generateFail;
                            stats.successRate = Number((generateSuccess / generateTotal).toFixed(3));

                            Skin.aggregate([
                                {
                                    "$group":
                                        {
                                            _id: "$type",
                                            duplicate: {$sum: "$duplicate"},
                                            views: {$sum: "$views"},
                                            count: {$sum: 1}
                                        }
                                }
                            ], function (err, agg) {
                                if (err) return console.log(err);
                                var user = agg[0];
                                var url = agg[1];
                                var upload = agg[2];

                                stats.genUpload = upload.count;
                                stats.genUrl = url.count;
                                stats.genUser = user.count;

                                stats.unique = user.count + url.count + upload.count;

                                stats.duplicate = user.duplicate + url.duplicate + upload.duplicate;
                                stats.views = user.views + url.views + upload.views;
                                stats.private = 0;
                                stats.withNames = 0;

                                stats.lastHour = 0;
                                stats.lastDay = 0;
                                stats.lastMonth = 0;
                                stats.lastYear = 0;

                                stats.viaApi = 0;
                                stats.viaWebsite = 0;

                                stats.total = stats.unique + stats.duplicate;

                                if (req.params.details) {
                                    var lastHour = new Date(new Date() - 3.6e+6) / 1000;
                                    var lastDay = new Date(new Date() - 8.64e+7) / 1000;
                                    var lastMonth = new Date(new Date() - 2.628e+9) / 1000;
                                    var lastYear = new Date(new Date() - 3.154e+10) / 1000;

                                    Skin.aggregate([
                                        {
                                            $group: {
                                                _id: null,
                                                lastYear: {$sum: {$cond: [{$gte: ["$time", lastYear]}, 1, 0]}},
                                                lastMonth: {$sum: {$cond: [{$gte: ["$time", lastMonth]}, 1, 0]}},
                                                lastDay: {$sum: {$cond: [{$gte: ["$time", lastDay]}, 1, 0]}},
                                                lastHour: {$sum: {$cond: [{$gte: ["$time", lastHour]}, 1, 0]}}
                                            }
                                        }
                                    ], function (err, agg1) {
                                        if (err) return console.log(err);

                                        stats.lastYear = agg1[0].lastYear;
                                        stats.lastMonth = agg1[0].lastMonth;
                                        stats.lastDay = agg1[0].lastDay;
                                        stats.lastHour = agg1[0].lastHour;

                                        res.json(stats);
                                    })

                                } else {
                                    res.json(stats);
                                }
                            })
                        })
                    })
                })
            })

        })
    });

    function buildSkinStats(skins) {
        var lastHour = new Date(new Date() - 3.6e+6) / 1000;
        var lastDay = new Date(new Date() - 8.64e+7) / 1000;
        var lastMonth = new Date(new Date() - 2.628e+9) / 1000;
        var lastYear = new Date(new Date() - 3.154e+10) / 1000;

        var stats = {};

        stats.unique = skins.length;

        stats.duplicate = 0;
        stats.views = 0;
        stats.private = 0;
        stats.withNames = 0;

        stats.lastHour = 0;
        stats.lastDay = 0;
        stats.lastMonth = 0;
        stats.lastYear = 0;

        stats.genUpload = 0;
        stats.genUrl = 0;
        stats.genUser = 0;

        stats.viaApi = 0;
        stats.viaWebsite = 0;

        var totalDuration = 0;
        skins.forEach(function (skin) {
            stats.duplicate += skin.duplicate;
            stats.views += skin.views;
            if (skin.visibility !== 0) stats.private++;
            if (skin.name && skin.name.length > 0) stats.withNames++;

            if (skin.time > lastHour) stats.lastHour++;
            if (skin.time > lastDay) stats.lastDay++;
            if (skin.time > lastMonth) stats.lastMonth++;
            if (skin.time > lastYear) stats.lastYear++;

            if (skin.type === "upload") stats.genUpload++;
            if (skin.type === "url") stats.genUrl++;
            if (skin.type === "user") stats.genUser++;

            if (skin.via === "api") stats.viaApi++;
            if (skin.via === "website") stats.viaWebsite++;

            if (skin.generateDuration)
                totalDuration += skin.generateDuration;
        });
        stats.total = stats.unique + stats.duplicate;

        stats.avgDuration = Number((totalDuration / skins.length).toFixed(4));

        return stats;
    }

    app.get("/get/id/:id", function (req, res) {
        Skin.findOne({id: req.params.id}).exec(function (err, skin) {
            if (err) return console.log(err);
            if (skin) {
                skin.views += 1;
                if (skin.model === "alex") {
                    skin.model = "slim";
                }
                skin.save(function (err, skin) {
                    if (err) return console.log(err);
                    res.json(Util.skinToJson(skin, 0));
                });
            } else {
                res.status(404).json({error: "Skin not found"});
            }
        })
    });

    app.get("/get/forTexture/:value/:signature?", function (req, res) {
        var search = {value: req.params.value};
        if (req.params.signature) {
            search.signature = req.params.signature;
        }
        Skin.findOne(search, function (err, skin) {
            if (err) return console.log(err);
            if (skin) {
                res.json(Util.skinToJson(skin, 0));
            } else {
                res.status(404).json({error: "Skin not found"});
            }
        });
    });

    app.get("/get/list/:page?", function (req, res) {
        var page = Math.max(req.params.page || 1, 1);
        var size = Math.max(req.query.size || 16, 1);
        size = Math.min(64, size);
        var sort = req.query.sort || -1;

        var query = {visibility: 0};
        if (req.query.filter) {
            query.name = {'$regex': ".*" + req.query.filter + ".*"};
        }
        if (req.query.cape) {
            if (req.query.cape === "true") {
                query.capeUrl = {'$ne': null};
            } else if (req.query.cape === "false") {
                query.capeUrl = {'$eq': null};
            }
        }
        Skin.count(query, function (err, count) {
            if (err) return console.log(err);
            Skin
                .find(query)
                .skip(size * (page - 1))
                .limit(size)
                .select({'_id': 0, id: 1, name: 1, url: 1, time: 1})
                .sort({time: sort})
                .lean()
                .exec(function (err, skins) {
                    if (err) return console.log(err)

                    res.json({
                        skins: skins,
                        page: {
                            index: page,
                            amount: Math.round(count / size),
                            total: count
                        },
                        filter: req.query.filter
                    })
                })
        });

    })

};
<commit_msg>add server to stats<commit_after>module.exports = function (app) {

    var Util = require("../util");
    var skinChanger = require("../generator/skinChanger");
    var auth = require("../generator/authentication");
    var dataFetcher = require("../generator/dataFetcher")
    var config = require("../config");

    // Schemas
    var Account = require("../db/schemas/account").Account;
    var Skin = require("../db/schemas/skin").Skin;
    var Traffic = require("../db/schemas/traffic").Traffic;
    var Stat = require("../db/schemas/stat").Stat;

    app.get("/get/delay", function (req, res) {
        var ip = req.realAddress;
        Util.getGeneratorDelay().then(function (delay) {
            Traffic.findOne({ip: ip}).lean().exec(function (err, traffic) {
                if (err) return console.log(err);
                if (traffic) {
                    res.json({delay: delay, next: (traffic.lastRequest.getTime() / 1000) + delay, nextRelative: ((traffic.lastRequest.getTime() / 1000) + delay) - (Date.now() / 1000)});
                } else {
                    res.json({delay: delay, next: Date.now() / 1000, nextRelative: 0});
                }
            })
        })
    })

    app.get("/get/stats/:details?", function (req, res) {
        var stats = {};

        stats.server = config.server;

        Util.getGeneratorDelay().then(function (delay) {
            stats.delay = delay;

            stats.queues = {
                auth:{
                    delay: config.requestQueue.auth,
                    size: auth.requestQueue.length
                },
                skinChanger: {
                    delay: config.requestQueue.skinChanger,
                    size: skinChanger.requestQueue.length
                }
            };
            stats.cache = {
               dataFetcher: Object.keys(dataFetcher.cache).length
            };

            // Skin.find({}, "duplicate views visibility time name type via generateDuration", function (err, skins) {
            //     if (err) return console.log(err);
            //     var skinStats = buildSkinStats(skins);
            //     Object.assign(stats, skinStats);
            //
            //     Account.count({enabled: true}, function (err, count) {
            //         if (err) return console.log(err);
            //         stats.accounts = count;
            //
            //         Stat.find({}, function (err, s) {
            //             var generateSuccess = 0;
            //             var generateFail = 0;
            //             s.forEach(function (stat) {
            //                 if (stat.key === "generate.success") generateSuccess = stat.value;
            //                 if (stat.key === "generate.fail") generateFail = stat.value;
            //             })
            //
            //             var generateTotal = generateSuccess + generateFail;
            //             stats.successRate = Number((generateSuccess / generateTotal).toFixed(3));
            //
            //             res.json(stats);
            //         })
            //     })
            // })

            Account.count({enabled: true}, function (err, count) {
                if (err) return console.log(err);
                stats.accounts = count;
                Account.count({enabled: true, errorCounter: {$lt: (config.errorThreshold || 10)}}, function (err, healthyCount) {
                    if (err) return console.log(err);
                    stats.healthyAccounts = healthyCount;
                    var time = Date.now()/1000;
                    Account.count({enabled: true, requestServer: {$in: [null, "default", config.server]}, lastUsed: {'$lt': (time - 100)}, forcedTimeoutAt: {'$lt': (time - 500)}, errorCounter: {'$lt': (config.errorThreshold||10)}},function(err,useableCount){
                        if (err) return console.log(err);
                        stats.useableAccounts = useableCount;
                        Stat.find({$or: [{key: "generate.success"}, {key: "generate.fail"}]}).lean().exec(function (err, s) {
                            if (err) return console.log(err);
                            var generateSuccess = 0;
                            var generateFail = 0;
                            s.forEach(function (stat) {
                                if (stat.key === "generate.success") generateSuccess = stat.value;
                                if (stat.key === "generate.fail") generateFail = stat.value;
                            })
                            var generateTotal = generateSuccess + generateFail;
                            stats.successRate = Number((generateSuccess / generateTotal).toFixed(3));

                            Skin.aggregate([
                                {
                                    "$group":
                                        {
                                            _id: "$type",
                                            duplicate: {$sum: "$duplicate"},
                                            views: {$sum: "$views"},
                                            count: {$sum: 1}
                                        }
                                }
                            ], function (err, agg) {
                                if (err) return console.log(err);
                                var user = agg[0];
                                var url = agg[1];
                                var upload = agg[2];

                                stats.genUpload = upload.count;
                                stats.genUrl = url.count;
                                stats.genUser = user.count;

                                stats.unique = user.count + url.count + upload.count;

                                stats.duplicate = user.duplicate + url.duplicate + upload.duplicate;
                                stats.views = user.views + url.views + upload.views;
                                stats.private = 0;
                                stats.withNames = 0;

                                stats.lastHour = 0;
                                stats.lastDay = 0;
                                stats.lastMonth = 0;
                                stats.lastYear = 0;

                                stats.viaApi = 0;
                                stats.viaWebsite = 0;

                                stats.total = stats.unique + stats.duplicate;

                                if (req.params.details) {
                                    var lastHour = new Date(new Date() - 3.6e+6) / 1000;
                                    var lastDay = new Date(new Date() - 8.64e+7) / 1000;
                                    var lastMonth = new Date(new Date() - 2.628e+9) / 1000;
                                    var lastYear = new Date(new Date() - 3.154e+10) / 1000;

                                    Skin.aggregate([
                                        {
                                            $group: {
                                                _id: null,
                                                lastYear: {$sum: {$cond: [{$gte: ["$time", lastYear]}, 1, 0]}},
                                                lastMonth: {$sum: {$cond: [{$gte: ["$time", lastMonth]}, 1, 0]}},
                                                lastDay: {$sum: {$cond: [{$gte: ["$time", lastDay]}, 1, 0]}},
                                                lastHour: {$sum: {$cond: [{$gte: ["$time", lastHour]}, 1, 0]}}
                                            }
                                        }
                                    ], function (err, agg1) {
                                        if (err) return console.log(err);

                                        stats.lastYear = agg1[0].lastYear;
                                        stats.lastMonth = agg1[0].lastMonth;
                                        stats.lastDay = agg1[0].lastDay;
                                        stats.lastHour = agg1[0].lastHour;

                                        res.json(stats);
                                    })

                                } else {
                                    res.json(stats);
                                }
                            })
                        })
                    })
                })
            })

        })
    });

    function buildSkinStats(skins) {
        var lastHour = new Date(new Date() - 3.6e+6) / 1000;
        var lastDay = new Date(new Date() - 8.64e+7) / 1000;
        var lastMonth = new Date(new Date() - 2.628e+9) / 1000;
        var lastYear = new Date(new Date() - 3.154e+10) / 1000;

        var stats = {};

        stats.unique = skins.length;

        stats.duplicate = 0;
        stats.views = 0;
        stats.private = 0;
        stats.withNames = 0;

        stats.lastHour = 0;
        stats.lastDay = 0;
        stats.lastMonth = 0;
        stats.lastYear = 0;

        stats.genUpload = 0;
        stats.genUrl = 0;
        stats.genUser = 0;

        stats.viaApi = 0;
        stats.viaWebsite = 0;

        var totalDuration = 0;
        skins.forEach(function (skin) {
            stats.duplicate += skin.duplicate;
            stats.views += skin.views;
            if (skin.visibility !== 0) stats.private++;
            if (skin.name && skin.name.length > 0) stats.withNames++;

            if (skin.time > lastHour) stats.lastHour++;
            if (skin.time > lastDay) stats.lastDay++;
            if (skin.time > lastMonth) stats.lastMonth++;
            if (skin.time > lastYear) stats.lastYear++;

            if (skin.type === "upload") stats.genUpload++;
            if (skin.type === "url") stats.genUrl++;
            if (skin.type === "user") stats.genUser++;

            if (skin.via === "api") stats.viaApi++;
            if (skin.via === "website") stats.viaWebsite++;

            if (skin.generateDuration)
                totalDuration += skin.generateDuration;
        });
        stats.total = stats.unique + stats.duplicate;

        stats.avgDuration = Number((totalDuration / skins.length).toFixed(4));

        return stats;
    }

    app.get("/get/id/:id", function (req, res) {
        Skin.findOne({id: req.params.id}).exec(function (err, skin) {
            if (err) return console.log(err);
            if (skin) {
                skin.views += 1;
                if (skin.model === "alex") {
                    skin.model = "slim";
                }
                skin.save(function (err, skin) {
                    if (err) return console.log(err);
                    res.json(Util.skinToJson(skin, 0));
                });
            } else {
                res.status(404).json({error: "Skin not found"});
            }
        })
    });

    app.get("/get/forTexture/:value/:signature?", function (req, res) {
        var search = {value: req.params.value};
        if (req.params.signature) {
            search.signature = req.params.signature;
        }
        Skin.findOne(search, function (err, skin) {
            if (err) return console.log(err);
            if (skin) {
                res.json(Util.skinToJson(skin, 0));
            } else {
                res.status(404).json({error: "Skin not found"});
            }
        });
    });

    app.get("/get/list/:page?", function (req, res) {
        var page = Math.max(req.params.page || 1, 1);
        var size = Math.max(req.query.size || 16, 1);
        size = Math.min(64, size);
        var sort = req.query.sort || -1;

        var query = {visibility: 0};
        if (req.query.filter) {
            query.name = {'$regex': ".*" + req.query.filter + ".*"};
        }
        if (req.query.cape) {
            if (req.query.cape === "true") {
                query.capeUrl = {'$ne': null};
            } else if (req.query.cape === "false") {
                query.capeUrl = {'$eq': null};
            }
        }
        Skin.count(query, function (err, count) {
            if (err) return console.log(err);
            Skin
                .find(query)
                .skip(size * (page - 1))
                .limit(size)
                .select({'_id': 0, id: 1, name: 1, url: 1, time: 1})
                .sort({time: sort})
                .lean()
                .exec(function (err, skins) {
                    if (err) return console.log(err)

                    res.json({
                        skins: skins,
                        page: {
                            index: page,
                            amount: Math.round(count / size),
                            total: count
                        },
                        filter: req.query.filter
                    })
                })
        });

    })

};
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import FontAwesome from 'react-fontawesome';
import { Button, ButtonGroup, Collapse } from 'react-bootstrap';
import { Timeline, TimelineEvent } from 'react-event-timeline';

import { defaultColorList } from '../../utils/Consts/ColorConsts';
import styles from './styles.module.css';

const NO_NEIGHBOR = 'NO_NEIGHBOR';

export default class EventTimeline extends React.Component {
  static propTypes = {
    dateProps: PropTypes.object,
    organizedNeighbors: PropTypes.object,
    renderNeighborGroupFn: PropTypes.func
  }

  constructor(props) {
    super(props);
    const datesToProps = this.getDatesToProps(props.organizedNeighbors, props.dateProps);
    const dateOptions = this.getDateOptions(datesToProps, props.dateProps);
    const selectedProps = this.getPropNamesFromDateProps(props.dateProps);
    this.state = {
      datesToProps,
      dateOptions,
      selectedProps,
      sortDesc: true,
      showDisplayPrefences: false
    };
  }

  componentWillReceiveProps(nextProps) {
    const datesToProps = this.getDatesToProps(nextProps.organizedNeighbors, nextProps.dateProps);
    const dateOptions = this.getDateOptions(datesToProps, nextProps.dateProps);
    const selectedProps = this.getPropNamesFromDateProps(nextProps.dateProps);
    this.setState({ datesToProps, dateOptions, selectedProps });
  }

  getPropNamesFromDateProps = (dateProps) => {
    const propNames = new Set();
    Object.keys(dateProps).forEach((entitySetId) => {
      dateProps[entitySetId].forEach((propertyTypeId) => {
        propNames.add(this.getPropAclKeyAsString(entitySetId, propertyTypeId));
      });
    });
    return propNames;
  }

  getPropDetails = (propertyType, propertyValues, dateProps, row) => {
    const dateDetails = {}
    const dateFqn = `${propertyType.type.namespace}.${propertyType.type.name}`;
    propertyValues[dateFqn].forEach((dateStr) => {
      const date = new Date(dateStr);
      const body = { propertyType, row };
      const dateValues = (dateProps[date]) ? dateProps[date].concat(body) : [body];
      dateDetails[date] = dateValues;
    });
    return dateDetails;
  }

  getPropDetailsForRow = (row, dateProps, datesToProps) => {
    const rowDatesToProps = datesToProps;
    if (dateProps[row.associationEntitySet.id]) {
      row.associationPropertyTypes.forEach((property) => {
        if (dateProps[row.associationEntitySet.id].includes(property.id)) {
          Object.assign(rowDatesToProps, this.getPropDetails(property, row.associationDetails, rowDatesToProps, row));
        }
      });
    }
    if (row.neighborEntitySet && dateProps[row.neighborEntitySet.id]) {
      row.neighborPropertyTypes.forEach((property) => {
        if (dateProps[row.neighborEntitySet.id].includes(property.id)) {
          Object.assign(rowDatesToProps, this.getPropDetails(property, row.neighborDetails, rowDatesToProps, row));
        }
      });
    }
    return rowDatesToProps;
  }

  getDatesToProps = (organizedNeighbors, dateProps) => {
    let datesToProps = {};
    Object.keys(organizedNeighbors).forEach((associationId) => {
      Object.keys(organizedNeighbors[associationId]).forEach((neighborId) => {
        if (dateProps[associationId] || dateProps[neighborId]) {
          organizedNeighbors[associationId][neighborId].forEach((row) => {
            datesToProps = this.getPropDetailsForRow(row, dateProps, datesToProps);
          });
        }
      });
    });
    return datesToProps;
  }

  getPropAclKeyAsString = (entitySetId, propertyTypeId) => {
    return `${entitySetId}|${propertyTypeId}`;
  }

  getPropAclKeyFromString = (aclKeyString) => {
    return aclKeyString.split('|');
  }

  getCheckboxTitle = (propAndRow) => {
    let esTitle = '';
    const assocTitle = propAndRow.row.associationEntitySet.title;
    const neighborTitle = (propAndRow.row.neighborEntitySet) ? propAndRow.row.neighborEntitySet.title : '';
    esTitle = (propAndRow.row.src) ? `${assocTitle} ${neighborTitle}` : `${neighborTitle} ${assocTitle}`;
    return `${esTitle}: ${propAndRow.propertyType.title}`;
  }

  getDateOptions = (datesToProps, dateProps) => {
    const dateOptions = {};
    Object.values(datesToProps).forEach((propAndRowList) => {
      propAndRowList.forEach((propAndRow) => {
        const propertyTypeId = propAndRow.propertyType.id;
        const associationEntitySet = propAndRow.row.associationEntitySet;
        if (dateProps[associationEntitySet.id] && dateProps[associationEntitySet.id].includes(propertyTypeId)) {
          const aclKeyString = this.getPropAclKeyAsString(associationEntitySet.id, propertyTypeId);
          if (!dateOptions[aclKeyString]) {
            const dateNum = Object.keys(dateOptions).length;
            const color = defaultColorList[dateNum % defaultColorList.length];
            const title = this.getCheckboxTitle(propAndRow);
            dateOptions[aclKeyString] = { title, color };
          }

        }

        const neighborEntitySet = propAndRow.row.neighborEntitySet;
        if (neighborEntitySet && dateProps[neighborEntitySet.id]
          && dateProps[neighborEntitySet.id].includes(propertyTypeId)) {
          const aclKeyString = this.getPropAclKeyAsString(neighborEntitySet.id, propertyTypeId);
          if (!dateOptions[aclKeyString]) {
            const dateNum = Object.keys(dateOptions).length;
            const color = defaultColorList[dateNum % defaultColorList.length];
            const title = this.getCheckboxTitle(propAndRow);
            dateOptions[aclKeyString] = { title, color };
          }
        }
      });
    });
    return dateOptions;
  }

  updateChecked = (e, aclKeyString) => {
    const selectedProps = this.state.selectedProps;
    if (e.target.checked) {
      selectedProps.add(aclKeyString);
    }
    else {
      selectedProps.delete(aclKeyString);
    }
    this.setState({ selectedProps });
  }

  renderCheckbox = (aclKeyString) => {
    const { title, color } = this.state.dateOptions[aclKeyString];
    return (

        <input
            type="checkbox"
            id={aclKeyString}
            name={aclKeyString}
            checked={this.state.selectedProps.has(aclKeyString)}
            onChange={(e) => {
              this.updateChecked(e, aclKeyString);
            }} />
        {title}

    );
  }

  renderSortOrderButtons = () => {
    return (
      <ButtonGroup>
        <Button
            onClick={() => {
              this.setState({ sortDesc: true });
            }}
            active={this.state.sortDesc}>
          <FontAwesome name="long-arrow-down" />
        </Button>
        <Button
            onClick={() => {
              this.setState({ sortDesc: false });
            }}
            active={!this.state.sortDesc}>
          <FontAwesome name="long-arrow-up" />
        </Button>
      </ButtonGroup>
    );
  }

  renderDisplayPreferencesBody = () => {
    const checkboxes = Object.keys(this.state.dateOptions).map((aclKeyString) => {
      return this.renderCheckbox(aclKeyString);
    });

    return (
      <Collapse in={this.state.showDisplayPrefences}>

Select properties to display on the timeline.

          {checkboxes}

Select sorted order.

          {this.renderSortOrderButtons()}

      </Collapse>
    );
  }

  renderDisplayPreferences = () => {
    const buttonName = (this.state.showDisplayPrefences) ? 'angle-up' : 'angle-down';
    return (

Display Preferences

        {this.renderDisplayPreferencesBody()}
        <button
            className={styles.expandDisplayPreferencesButton}
            onClick={() => {
              this.setState({ showDisplayPrefences: !this.state.showDisplayPrefences });
            }}>
          <FontAwesome name={buttonName} size="2x" />
        </button>

    );
  }

  renderEvent = (date, dateDetails, key) => {
    const propertyType = dateDetails.propertyType;
    const associationEntitySet = dateDetails.row.associationEntitySet;
    const neighborEntitySet = dateDetails.row.neighborEntitySet;
    const associationAclKey = this.getPropAclKeyAsString(associationEntitySet.id, propertyType.id);
    const neighborAclKey = (neighborEntitySet) ?
      this.getPropAclKeyAsString(neighborEntitySet.id, propertyType.id) : null;
    let color;
    if (neighborEntitySet && this.state.selectedProps.has(neighborAclKey)) {
      color = this.state.dateOptions[neighborAclKey].color;
    }
    else if (this.state.selectedProps.has(associationAclKey)) {
      color = this.state.dateOptions[associationAclKey].color;
    }
    else return null;

    const neighborId = (neighborEntitySet) ? neighborEntitySet.id : NO_NEIGHBOR;
    const neighborTitle = (neighborEntitySet) ? neighborEntitySet.title : '';
    const title = (dateDetails.row.src)
      ? `${associationEntitySet.title} ${neighborTitle}: ${propertyType.title}`
      : `${neighborTitle} ${associationEntitySet.title}: ${propertyType.title}`;
    const dateObj = new Date(date);
    const formattedDate = `${dateObj.toDateString()} ${dateObj.toLocaleTimeString()}`;
    return (
      <TimelineEvent
          key={key}
          title={
{title}
}
          createdAt={
{formattedDate}
}
          contentStyle={{ boxShadow: 'none' }}
          iconColor={color}>
        {this.props.renderNeighborGroupFn([dateDetails.row], neighborId, false)}
      </TimelineEvent>
    );
  }

  renderTimelineEvents = (datesToProps) => {
    const orderedDates = Object.keys(datesToProps).sort((date1, date2) => {
      if (this.state.sortDesc) return new Date(date2).getTime() - new Date(date1).getTime();
      return new Date(date1).getTime() - new Date(date2).getTime();
    });
    const events = [];
    orderedDates.forEach((date, dateIndex) => {
      datesToProps[date].forEach((dateDetails, detailsIndex) => {
        events.push(this.renderEvent(date, dateDetails, `${dateIndex}-${detailsIndex}`));
      });
    });
    return events;
  }

  renderTimeline = () => {
    if (this.state.selectedProps.size === 0) return null;
    return (

        <Timeline>
          {this.renderTimelineEvents(this.state.datesToProps)}
        </Timeline>

    );
  }

  render() {
    return (

        {this.renderDisplayPreferences()}
        {this.renderTimeline()}

    );
  }
}
<commit_msg>check that a date property exists in the row (otherwise it'll be broken)<commit_after>import React, { PropTypes } from 'react';
import FontAwesome from 'react-fontawesome';
import { Button, ButtonGroup, Collapse } from 'react-bootstrap';
import { Timeline, TimelineEvent } from 'react-event-timeline';

import { defaultColorList } from '../../utils/Consts/ColorConsts';
import styles from './styles.module.css';

const NO_NEIGHBOR = 'NO_NEIGHBOR';

export default class EventTimeline extends React.Component {
  static propTypes = {
    dateProps: PropTypes.object,
    organizedNeighbors: PropTypes.object,
    renderNeighborGroupFn: PropTypes.func
  }

  constructor(props) {
    super(props);
    const datesToProps = this.getDatesToProps(props.organizedNeighbors, props.dateProps);
    const dateOptions = this.getDateOptions(datesToProps, props.dateProps);
    const selectedProps = this.getPropNamesFromDateProps(props.dateProps);
    this.state = {
      datesToProps,
      dateOptions,
      selectedProps,
      sortDesc: true,
      showDisplayPrefences: false
    };
  }

  componentWillReceiveProps(nextProps) {
    const datesToProps = this.getDatesToProps(nextProps.organizedNeighbors, nextProps.dateProps);
    const dateOptions = this.getDateOptions(datesToProps, nextProps.dateProps);
    const selectedProps = this.getPropNamesFromDateProps(nextProps.dateProps);
    this.setState({ datesToProps, dateOptions, selectedProps });
  }

  getPropNamesFromDateProps = (dateProps) => {
    const propNames = new Set();
    Object.keys(dateProps).forEach((entitySetId) => {
      dateProps[entitySetId].forEach((propertyTypeId) => {
        propNames.add(this.getPropAclKeyAsString(entitySetId, propertyTypeId));
      });
    });
    return propNames;
  }

  getPropDetails = (propertyType, propertyValues, dateProps, row) => {
    const dateDetails = {};
    const dateFqn = `${propertyType.type.namespace}.${propertyType.type.name}`;
    if (propertyValues[dateFqn]) {
      propertyValues[dateFqn].forEach((dateStr) => {
        const date = new Date(dateStr);
        const body = { propertyType, row };
        const dateValues = (dateProps[date]) ? dateProps[date].concat(body) : [body];
        dateDetails[date] = dateValues;
      });
    }
    return dateDetails;
  }

  getPropDetailsForRow = (row, dateProps, datesToProps) => {
    const rowDatesToProps = datesToProps;
    if (dateProps[row.associationEntitySet.id]) {
      row.associationPropertyTypes.forEach((property) => {
        if (dateProps[row.associationEntitySet.id].includes(property.id)) {
          Object.assign(rowDatesToProps, this.getPropDetails(property, row.associationDetails, rowDatesToProps, row));
        }
      });
    }
    if (row.neighborEntitySet && dateProps[row.neighborEntitySet.id]) {
      row.neighborPropertyTypes.forEach((property) => {
        if (dateProps[row.neighborEntitySet.id].includes(property.id)) {
          Object.assign(rowDatesToProps, this.getPropDetails(property, row.neighborDetails, rowDatesToProps, row));
        }
      });
    }
    return rowDatesToProps;
  }

  getDatesToProps = (organizedNeighbors, dateProps) => {
    let datesToProps = {};
    Object.keys(organizedNeighbors).forEach((associationId) => {
      Object.keys(organizedNeighbors[associationId]).forEach((neighborId) => {
        if (dateProps[associationId] || dateProps[neighborId]) {
          organizedNeighbors[associationId][neighborId].forEach((row) => {
            datesToProps = this.getPropDetailsForRow(row, dateProps, datesToProps);
          });
        }
      });
    });
    return datesToProps;
  }

  getPropAclKeyAsString = (entitySetId, propertyTypeId) => {
    return `${entitySetId}|${propertyTypeId}`;
  }

  getPropAclKeyFromString = (aclKeyString) => {
    return aclKeyString.split('|');
  }

  getCheckboxTitle = (propAndRow) => {
    let esTitle = '';
    const assocTitle = propAndRow.row.associationEntitySet.title;
    const neighborTitle = (propAndRow.row.neighborEntitySet) ? propAndRow.row.neighborEntitySet.title : '';
    esTitle = (propAndRow.row.src) ? `${assocTitle} ${neighborTitle}` : `${neighborTitle} ${assocTitle}`;
    return `${esTitle}: ${propAndRow.propertyType.title}`;
  }

  getDateOptions = (datesToProps, dateProps) => {
    const dateOptions = {};
    Object.values(datesToProps).forEach((propAndRowList) => {
      propAndRowList.forEach((propAndRow) => {
        const propertyTypeId = propAndRow.propertyType.id;
        const associationEntitySet = propAndRow.row.associationEntitySet;
        if (dateProps[associationEntitySet.id] && dateProps[associationEntitySet.id].includes(propertyTypeId)) {
          const aclKeyString = this.getPropAclKeyAsString(associationEntitySet.id, propertyTypeId);
          if (!dateOptions[aclKeyString]) {
            const dateNum = Object.keys(dateOptions).length;
            const color = defaultColorList[dateNum % defaultColorList.length];
            const title = this.getCheckboxTitle(propAndRow);
            dateOptions[aclKeyString] = { title, color };
          }

        }

        const neighborEntitySet = propAndRow.row.neighborEntitySet;
        if (neighborEntitySet && dateProps[neighborEntitySet.id]
          && dateProps[neighborEntitySet.id].includes(propertyTypeId)) {
          const aclKeyString = this.getPropAclKeyAsString(neighborEntitySet.id, propertyTypeId);
          if (!dateOptions[aclKeyString]) {
            const dateNum = Object.keys(dateOptions).length;
            const color = defaultColorList[dateNum % defaultColorList.length];
            const title = this.getCheckboxTitle(propAndRow);
            dateOptions[aclKeyString] = { title, color };
          }
        }
      });
    });
    return dateOptions;
  }

  updateChecked = (e, aclKeyString) => {
    const selectedProps = this.state.selectedProps;
    if (e.target.checked) {
      selectedProps.add(aclKeyString);
    }
    else {
      selectedProps.delete(aclKeyString);
    }
    this.setState({ selectedProps });
  }

  renderCheckbox = (aclKeyString) => {
    const { title, color } = this.state.dateOptions[aclKeyString];
    return (

        <input
            type="checkbox"
            id={aclKeyString}
            name={aclKeyString}
            checked={this.state.selectedProps.has(aclKeyString)}
            onChange={(e) => {
              this.updateChecked(e, aclKeyString);
            }} />
        {title}

    );
  }

  renderSortOrderButtons = () => {
    return (
      <ButtonGroup>
        <Button
            onClick={() => {
              this.setState({ sortDesc: true });
            }}
            active={this.state.sortDesc}>
          <FontAwesome name="long-arrow-down" />
        </Button>
        <Button
            onClick={() => {
              this.setState({ sortDesc: false });
            }}
            active={!this.state.sortDesc}>
          <FontAwesome name="long-arrow-up" />
        </Button>
      </ButtonGroup>
    );
  }

  renderDisplayPreferencesBody = () => {
    const checkboxes = Object.keys(this.state.dateOptions).map((aclKeyString) => {
      return this.renderCheckbox(aclKeyString);
    });

    return (
      <Collapse in={this.state.showDisplayPrefences}>

Select properties to display on the timeline.

          {checkboxes}

Select sorted order.

          {this.renderSortOrderButtons()}

      </Collapse>
    );
  }

  renderDisplayPreferences = () => {
    const buttonName = (this.state.showDisplayPrefences) ? 'angle-up' : 'angle-down';
    return (

Display Preferences

        {this.renderDisplayPreferencesBody()}
        <button
            className={styles.expandDisplayPreferencesButton}
            onClick={() => {
              this.setState({ showDisplayPrefences: !this.state.showDisplayPrefences });
            }}>
          <FontAwesome name={buttonName} size="2x" />
        </button>

    );
  }

  renderEvent = (date, dateDetails, key) => {
    const propertyType = dateDetails.propertyType;
    const associationEntitySet = dateDetails.row.associationEntitySet;
    const neighborEntitySet = dateDetails.row.neighborEntitySet;
    const associationAclKey = this.getPropAclKeyAsString(associationEntitySet.id, propertyType.id);
    const neighborAclKey = (neighborEntitySet) ?
      this.getPropAclKeyAsString(neighborEntitySet.id, propertyType.id) : null;
    let color;
    if (neighborEntitySet && this.state.selectedProps.has(neighborAclKey)) {
      color = this.state.dateOptions[neighborAclKey].color;
    }
    else if (this.state.selectedProps.has(associationAclKey)) {
      color = this.state.dateOptions[associationAclKey].color;
    }
    else return null;

    const neighborId = (neighborEntitySet) ? neighborEntitySet.id : NO_NEIGHBOR;
    const neighborTitle = (neighborEntitySet) ? neighborEntitySet.title : '';
    const title = (dateDetails.row.src)
      ? `${associationEntitySet.title} ${neighborTitle}: ${propertyType.title}`
      : `${neighborTitle} ${associationEntitySet.title}: ${propertyType.title}`;
    const dateObj = new Date(date);
    const formattedDate = `${dateObj.toDateString()} ${dateObj.toLocaleTimeString()}`;
    return (
      <TimelineEvent
          key={key}
          title={
{title}
}
          createdAt={
{formattedDate}
}
          contentStyle={{ boxShadow: 'none' }}
          iconColor={color}>
        {this.props.renderNeighborGroupFn([dateDetails.row], neighborId, false)}
      </TimelineEvent>
    );
  }

  renderTimelineEvents = (datesToProps) => {
    const orderedDates = Object.keys(datesToProps).sort((date1, date2) => {
      if (this.state.sortDesc) return new Date(date2).getTime() - new Date(date1).getTime();
      return new Date(date1).getTime() - new Date(date2).getTime();
    });
    const events = [];
    orderedDates.forEach((date, dateIndex) => {
      datesToProps[date].forEach((dateDetails, detailsIndex) => {
        events.push(this.renderEvent(date, dateDetails, `${dateIndex}-${detailsIndex}`));
      });
    });
    return events;
  }

  renderTimeline = () => {
    if (this.state.selectedProps.size === 0) return null;
    return (

        <Timeline>
          {this.renderTimelineEvents(this.state.datesToProps)}
        </Timeline>

    );
  }

  render() {
    return (

        {this.renderDisplayPreferences()}
        {this.renderTimeline()}

    );
  }
}
<|endoftext|>