<commit_before>class Composer {

  /**
   * Initialize a new `Composer`.
   * @param {(Function|Function[])} handlers - middleware
   * @api public
   */
  constructor (...handlers) {
    this.handlers = handlers
  }

  /**
   * Register a middleware.
   *
   * @param {Function} fns - middleware
   * @return {Composer} self
   * @api public
   */
  use (...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.handlers = this.handlers.concat(fns)
    return this
  }

  /**
   * Use the given middleware as handler for `updateType`.
   *
   * @param {string} updateType - Update type
   * @param {(Function|Function[])} fns - middleware
   * @return {Composer} self
   * @api public
   */
  on (updateTypes, ...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.use(Composer.mount(updateTypes, Composer.compose(fns)))
    return this
  }

  /**
   * Use the given middleware as handler for text messages.
   *
   * @param {(string|RegEx)} triggers - Text triggers
   * @param {(Function|Function[])} middleware - middleware
   * @return {Composer} self
   * @api public
   */
  hears (triggers, ...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.use(Composer.hears(triggers, Composer.compose(fns)))
    return this
  }

  /**
   * Just `hears` alias.
   *
   * @api public
   */
  command (...args) {
    return this.hears(...args)
  }

  /**
   * Use the given middleware as handler for callback_query data.
   *
   * @param {(string|RegEx)} triggers - Text triggers
   * @param {(Function|Function[])} middleware - middleware
   * @return {Composer} self
   * @api public
   */
  action (triggers, ...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.use(Composer.action(triggers, Composer.compose(fns)))
    return this
  }

  /**
   * Composed middleware.
   *
   * @api public
   */
  middleware () {
    return Composer.compose(this.handlers)
  }

  /**
   * PassThru `middleware` factory.
   *
   * @api public
   */
  static passThru () {
    return (ctx, next) => next()
  }

  /**
   * Tap `middleware` factory.
   *
   * @param {Function} fn
   * @api public
   */
  static tap (fn) {
    return (ctx, next) => {
      setImmediate(fn, ctx)
      return next()
    }
  }

  /**
   * Log `middleware` factory.
   *
   * @param {Function} fn
   * @api public
   */
  static log (fn = console.log) {
    return (ctx, next) => {
      try {
        fn(JSON.stringify(ctx.update, null, 2))
      } catch (err) {
        console.error('Telegraf: error in `log` handler', err)
      }
      return next()
    }
  }

  /**
   * Optional `middleware` factory.
   *
   * @param {(bool|Function)} test
   * @api public
   */
  static optional (test, ...fns) {
    if (typeof test !== 'function') {
      return test ? Composer.compose(fns) : Composer.passThru()
    }
    const middleware = Composer.compose(fns)
    return (ctx, next) => test(ctx) ? middleware(ctx, next) : next()
  }

  /**
   * Branch `middleware` factory.
   *
   * @param {(bool|Function)} test
   * @api public
   */
  static branch (test, trueMiddleware, falseMiddleware) {
    if (typeof test !== 'function') {
      return test ? trueMiddleware : falseMiddleware
    }
    return (ctx, next) => test(ctx) ? trueMiddleware(ctx, next) : falseMiddleware(ctx, next)
  }

  /**
   * Compose `middlewares` returning
   * a fully valid middleware comprised
   * of all those which are passed.
   *
   * @param {Function[]} middlewares
   * @return {Function}
   * @api public
   */
  static compose (middlewares) {
    if (!Array.isArray(middlewares)) {
      middlewares = [middlewares]
    }
    if (middlewares.includes((fn) => !(typeof fn === 'function' || fn instanceof Composer))) {
      throw new TypeError('Middleware must be composed of functions')
    }
    return (ctx, next) => {
      let index = -1
      return dispatch(0)
      function dispatch (i) {
        if (i <= index) {
          return Promise.reject(new Error('next() called multiple times'))
        }
        index = i
        let handler = middlewares[i] || next
        if (handler instanceof Composer) {
          handler = handler.middleware()
        }
        if (!handler) {
          return Promise.resolve()
        }
        try {
          return Promise.resolve(handler(ctx, () => dispatch(i + 1)))
        } catch (err) {
          return Promise.reject(err)
        }
      }
    }
  }

  /**
   * Generates `middleware` for handling provided update types.
   *
   * @param {string|string[]} updateTypes
   * @param {Function|} middleware - middleware
   * @api public
   */
  static mount (updateTypes, middleware) {
    if (!Array.isArray(updateTypes)) {
      updateTypes = [updateTypes]
    }
    return Composer.optional((ctx) => updateTypes.includes(ctx.updateType) || updateTypes.includes(ctx.updateSubType), middleware)
  }

  /**
   * Generates `middleware` for handling `text` messages with regular expressions.
   *
   * @param {(string|RegEx|Function)[]} triggers
   * @param {(Function)} middleware - middleware
   * @api public
   */
  static hears (triggers, middleware) {
    const tests = Composer.normalizeTriggers(triggers)
    return Composer.mount('text', (ctx, next) => {
      const match = Composer.testTriggers(ctx.message.text, tests, ctx)
      if (!match) {
        return next()
      }
      ctx.match = match
      return middleware(ctx, next)
    })
  }

  /**
   * Generates `middleware` for handling `callbackQuery` actions with regular expressions.
   *
   * @param {(string|RegEx|Function)[]} triggers
   * @param {(Function)} middleware - middleware
   * @api public
   */
  static action (triggers, middleware) {
    const tests = Composer.normalizeTriggers(triggers)
    return Composer.mount('callback_query', (ctx, next) => {
      const match = Composer.testTriggers(ctx.callbackQuery.data, tests, ctx)
      if (!match) {
        return next()
      }
      ctx.match = match
      return middleware(ctx, next)
    })
  }

  static normalizeTriggers (triggers) {
    if (!Array.isArray(triggers)) {
      triggers = [triggers]
    }
    return triggers.map((trigger) => {
      if (!trigger) {
        throw new Error('Invalid trigger')
      }
      if (typeof trigger === 'function') {
        return trigger
      }
      const regex = trigger instanceof RegExp ? trigger : Composer.stringToRegEx(trigger + '')
      return (string) => string && regex.exec(string)
    })
  }

  static testTriggers (string, tests, ctx) {
    for (let test of tests) {
      const result = test(string, ctx)
      if (result) {
        return result
      }
    }
  }

  static stringToRegEx (string) {
    const escaped = string.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
    return new RegExp(`^${escaped}$`)
  }
}

module.exports = Composer
<commit_msg>update composer<commit_after>class Composer {

  /**
   * Initialize a new `Composer`.
   * @param {(Function|Function[])} handlers - middleware
   * @api public
   */
  constructor (...handlers) {
    this.handlers = handlers
  }

  /**
   * Register a middleware.
   *
   * @param {Function} fns - middleware
   * @return {Composer} self
   * @api public
   */
  use (...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.handlers = this.handlers.concat(fns)
    return this
  }

  /**
   * Use the given middleware as handler for `updateType`.
   *
   * @param {string} updateType - Update type
   * @param {(Function|Function[])} fns - middleware
   * @return {Composer} self
   * @api public
   */
  on (updateTypes, ...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.use(Composer.mount(updateTypes, Composer.compose(fns)))
    return this
  }

  /**
   * Use the given middleware as handler for text messages.
   *
   * @param {(string|RegEx)} triggers - Text triggers
   * @param {(Function|Function[])} middleware - middleware
   * @return {Composer} self
   * @api public
   */
  hears (triggers, ...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.use(Composer.hears(triggers, Composer.compose(fns)))
    return this
  }

  /**
   * Just `hears` alias.
   *
   * @api public
   */
  command (...args) {
    return this.hears(...args)
  }

  /**
   * Use the given middleware as handler for callback_query data.
   *
   * @param {(string|RegEx)} triggers - Text triggers
   * @param {(Function|Function[])} middleware - middleware
   * @return {Composer} self
   * @api public
   */
  action (triggers, ...fns) {
    if (fns.length === 0) {
      throw new TypeError('At least one Middleware must be provided')
    }
    this.use(Composer.action(triggers, Composer.compose(fns)))
    return this
  }

  /**
   * Composed middleware.
   *
   * @api public
   */
  middleware () {
    return Composer.compose(this.handlers)
  }

  /**
   * PassThru `middleware` factory.
   *
   * @api public
   */
  static passThru () {
    return (ctx, next) => next()
  }

  /**
   * Tap `middleware` factory.
   *
   * @param {Function} fn
   * @api public
   */
  static tap (fn) {
    return (ctx, next) => {
      setImmediate(fn, ctx)
      return next()
    }
  }

  /**
   * Log `middleware` factory.
   *
   * @param {Function} fn
   * @api public
   */
  static log (fn = console.log) {
    return (ctx, next) => {
      try {
        fn(JSON.stringify(ctx.update, null, 2))
      } catch (err) {
        console.error('Telegraf: error in `log` handler', err)
      }
      return next()
    }
  }

  /**
   * Optional `middleware` factory.
   *
   * @param {(bool|Function)} test
   * @api public
   */
  static optional (test, ...fns) {
    if (typeof test !== 'function') {
      return test ? Composer.compose(fns) : Composer.passThru()
    }
    const middleware = Composer.compose(fns)
    return (ctx, next) => test(ctx) ? middleware(ctx, next) : next()
  }

  /**
   * Branch `middleware` factory.
   *
   * @param {(bool|Function)} test
   * @api public
   */
  static branch (test, trueMiddleware, falseMiddleware) {
    if (typeof test !== 'function') {
      return test ? trueMiddleware : falseMiddleware
    }
    return (ctx, next) => test(ctx) ? trueMiddleware(ctx, next) : falseMiddleware(ctx, next)
  }

  /**
   * Compose `middlewares` returning
   * a fully valid middleware comprised
   * of all those which are passed.
   *
   * @param {Function[]} middlewares
   * @return {Function}
   * @api public
   */
  static compose (middlewares) {
    if (!Array.isArray(middlewares)) {
      middlewares = [middlewares]
    }
    if (middlewares.includes((fn) => !fn || !(typeof fn === 'function' || typeof fn.middleware === 'function'))) {
      throw new TypeError('Middleware must be composed of functions')
    }
    return (ctx, next) => {
      let index = -1
      return dispatch(0)
      function dispatch (i) {
        if (i <= index) {
          return Promise.reject(new Error('next() called multiple times'))
        }
        index = i
        let handler = middlewares[i] || next
        if (handler.middleware) {
          handler = handler.middleware()
        }
        if (!handler) {
          return Promise.resolve()
        }
        try {
          return Promise.resolve(handler(ctx, () => dispatch(i + 1)))
        } catch (err) {
          return Promise.reject(err)
        }
      }
    }
  }

  /**
   * Generates `middleware` for handling provided update types.
   *
   * @param {string|string[]} updateTypes
   * @param {Function|} middleware - middleware
   * @api public
   */
  static mount (updateTypes, middleware) {
    if (!Array.isArray(updateTypes)) {
      updateTypes = [updateTypes]
    }
    return Composer.optional((ctx) => updateTypes.includes(ctx.updateType) || updateTypes.includes(ctx.updateSubType), middleware)
  }

  /**
   * Generates `middleware` for handling `text` messages with regular expressions.
   *
   * @param {(string|RegEx|Function)[]} triggers
   * @param {(Function)} middleware - middleware
   * @api public
   */
  static hears (triggers, middleware) {
    const tests = Composer.normalizeTriggers(triggers)
    return Composer.mount('text', (ctx, next) => {
      const match = Composer.testTriggers(ctx.message.text, tests, ctx)
      if (!match) {
        return next()
      }
      ctx.match = match
      return middleware(ctx, next)
    })
  }

  /**
   * Generates `middleware` for handling `callbackQuery` actions with regular expressions.
   *
   * @param {(string|RegEx|Function)[]} triggers
   * @param {(Function)} middleware - middleware
   * @api public
   */
  static action (triggers, middleware) {
    const tests = Composer.normalizeTriggers(triggers)
    return Composer.mount('callback_query', (ctx, next) => {
      const match = Composer.testTriggers(ctx.callbackQuery.data, tests, ctx)
      if (!match) {
        return next()
      }
      ctx.match = match
      return middleware(ctx, next)
    })
  }

  static normalizeTriggers (triggers) {
    if (!Array.isArray(triggers)) {
      triggers = [triggers]
    }
    return triggers.map((trigger) => {
      if (!trigger) {
        throw new Error('Invalid trigger')
      }
      if (typeof trigger === 'function') {
        return trigger
      }
      const regex = trigger instanceof RegExp ? trigger : Composer.stringToRegEx(trigger + '')
      return (string) => string && regex.exec(string)
    })
  }

  static testTriggers (string, tests, ctx) {
    for (let test of tests) {
      const result = test(string, ctx)
      if (result) {
        return result
      }
    }
  }

  static stringToRegEx (string) {
    const escaped = string.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
    return new RegExp(`^${escaped}$`)
  }
}

module.exports = Composer
<|endoftext|>


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

export default class SchoolDisciplineDashboard extends React.Component {

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

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

  filterStudents(students) {
    const {selectedChart, selectedCategory} = this.state;
    return students.filter(student => {
      if (selectedCategory && selectedChart === 'grade' && student.grade !== selectedCategory) return false;
      if (selectedCategory && selectedChart === 'homeroom_label' && student.homeroom_label !== selectedCategory) return false;
      if (selectedCategory && !this.filterIncidents(student.discipline_incidents, true).length) return false;
      if (!this.filterIncidents(student.discipline_incidents, false).length) return false;
      return true;
    });
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            <FilterBar style={styles.timeRange} >
              <SelectDisciplineIncidentType
                type={this.state.selectedIncidentCode || 'All'}
                onChange={this.onIncidentTypeChange}
                types={incidentTypes}/>
            </FilterBar>
            <FilterBar style={styles.timeRange}>
              <SelectTimeRange
                timeRangeKey={timeRangeKey}
                onChange={this.onTimeRangeKeyChanged} />
            </FilterBar>

              {this.renderStudentDisciplineTable(dashboardStudents)}

                  Break down by:

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

             {this.renderDisciplineChart(dashboardStudents, selectedChart)}

      </EscapeListener>
    );
  }

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

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

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

function initialState() {
  return {
    timeRangeKey: TIME_RANGE_45_DAYS_AGO,
    selectedChart: 'incident_location',
    selectedIncidentCode: ALL,
    selectedCategory: null
  };
}
<commit_msg>removing incident type from the chart options<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import moment from 'moment';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import SelectTimeRange, {
  momentRange,
  TIME_RANGE_45_DAYS_AGO
} from '../../components/SelectTimeRange';
import {ALL} from '../../components/SimpleFilterSelect';
import SelectDisciplineIncidentType from '../../components/SelectDisciplineIncidentType';
import memoizer from '../../helpers/memoizer';
import FilterBar from '../../components/FilterBar';
import {sortByGrade} from '../../helpers/SortHelpers';
import ExperimentalBanner from '../../components/ExperimentalBanner';
import SectionHeading from '../../components/SectionHeading';
import EscapeListener from '../../components/EscapeListener';
import StudentsTable from '../StudentsTable';
import DashboardBarChart from '../DashboardBarChart';
import * as dashboardStyles from '../dashboardStyles';

export default class SchoolDisciplineDashboard extends React.Component {

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

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

  filterStudents(students) {
    const {selectedChart, selectedCategory} = this.state;
    return students.filter(student => {
      if (selectedCategory && selectedChart === 'grade' && student.grade !== selectedCategory) return false;
      if (selectedCategory && selectedChart === 'homeroom_label' && student.homeroom_label !== selectedCategory) return false;
      if (selectedCategory && !this.filterIncidents(student.discipline_incidents, true).length) return false;
      if (!this.filterIncidents(student.discipline_incidents, false).length) return false;
      return true;
    });
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            <FilterBar style={styles.timeRange} >
              <SelectDisciplineIncidentType
                type={this.state.selectedIncidentCode || 'All'}
                onChange={this.onIncidentTypeChange}
                types={incidentTypes}/>
            </FilterBar>
            <FilterBar style={styles.timeRange}>
              <SelectTimeRange
                timeRangeKey={timeRangeKey}
                onChange={this.onTimeRangeKeyChanged} />
            </FilterBar>

              {this.renderStudentDisciplineTable(dashboardStudents)}

                  Break down by:

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

             {this.renderDisciplineChart(dashboardStudents, selectedChart)}

      </EscapeListener>
    );
  }

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

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

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

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


<commit_before>/**
 * @file Functions to deal with copy and paste.
 * @copyright This file is part of Fidus Writer.
 *
 * Copyright (C) 2013 Takuto Kojima, Johannes Wilm.
 *
 * @license This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 */
(function() {
    var exports = this,
        /**
         * Functions related to pasting in the editor. TODO
         * @namespace paste
         */
        paste = {};

    paste.addPasteContents = function(pasteElement, footnotes) {
        var selection = rangy.getSelection(),
            range = selection.getRangeAt(0),
            scrollIntoView = false,
            startPaste, // the first node to be pasted
            endPaste, // The last node to be pasted.
            newNode;
        if (editorHelpers.TEXT_ELEMENTS.indexOf(jQuery(range.startContainer).closest('.editable')[0].id)!=-1) {
            // We are inside an element that does not allow complex HTML.
            // Paste therefore only text contents and forget about footnotes.
            pasteElement.textContent = pasteElement.textContent;
            footnotes = false;
        }
        if (!pasteElement.firstChild) {
            // paste is empty.
            return true;
        }
        startPaste = pasteElement.firstChild;
        endPaste = pasteElement.lastChild;

        while (pasteElement.firstChild) {
            if (pasteElement.childNodes.length === 1) {
                scrollIntoView = true;
            }
            manualEdits.insert(pasteElement.firstChild, range, scrollIntoView);
        }
        if (startPaste.nodeName==='FIGURE' && ((!startPaste.previousSibling) || editorHelpers.TEXT_BLOCK_ELEMENTS.indexOf(startPaste.previousSibling.nodeName) === -1)) {
            // If there is no text block node before a figure at the start of the paste, add it now.
            newNode = document.createElement('p');
            newNode.innerHTML = '
';
            startPaste.parentNode.insertBefore(newNode, startPaste);
        } else if (startPaste.nodeName != 'FIGURE' && startPaste.previousSibling && editorHelpers.TEXT_BLOCK_ELEMENTS.indexOf(startPaste.previousSibling.nodeName) != -1 && startPaste.previousSibling.textContent.trim().length === 0) {
            // If there was an empty paragraph before the first pasted paragraph, remove it.
            startPaste.parentNode.removeChild(startPaste.previousSibling);
        }

        if (endPaste.nodeName==='FIGURE' && ((!endPaste.nextSibling) || editorHelpers.TEXT_BLOCK_ELEMENTS.indexOf(endPaste.nextSibling.nodeName) === -1)) {
            // If there is no text block node after a figure at the end of the paste, add it now.
            newNode = document.createElement('p');
            newNode.innerHTML = '
';
            endPaste.parentNode.insertBefore(newNode, endPaste.nextSibling);
        }
        if (footnotes) {
            editorEscapes.reset();
        }

        return true;
    };

    paste.insertText = function (textString) {
        var pasteElement = document.createElement('div');
            pasteElement.textcontent = textString;
        paste.addPasteContents(pasteElement, false);
    };

    paste.insertHTML = function (htmlString) {
        var pasteElement = document.createElement('div'),
            htmlCleaner;
            pasteElement.innerHTML = htmlString;
            htmlCleaner = new cleanHTML(pasteElement);
        paste.addPasteContents(pasteElement, htmlCleaner.footnotes);
    };

    paste.recentlyPasted = false; // prevent double pasting by checking whether paste has been done recently

    paste.handlePaste = function(event) {
        // We cancel the paste event, copy clipboard data, clean it, insert it
        var htmlString;
        event.stopPropagation();
        event.preventDefault();
        if (!paste.recentlyPasted) {
            paste.recentlyPasted = true;
            setTimeout(function() {
                paste.recentlyPasted = false;
            }, 1)

            if (/text\/html/.test(event.clipboardData.types)) {
                paste.insertHTML(event.clipboardData.getData(
                    'text/html'));
            } else if (/text\/plain/.test(event.clipboardData.types)) {
                paste.insertText(event.clipboardData.getData(
                    'text/plain'));
            }
        }
    };

    exports.paste = paste;

}).call(this);
<commit_msg>allow pasting of non-html content<commit_after>/**
 * @file Functions to deal with copy and paste.
 * @copyright This file is part of Fidus Writer.
 *
 * Copyright (C) 2013 Takuto Kojima, Johannes Wilm.
 *
 * @license This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 */
(function() {
    var exports = this,
        /**
         * Functions related to pasting in the editor. TODO
         * @namespace paste
         */
        paste = {};

    paste.addPasteContents = function(pasteElement, footnotes) {
        var selection = rangy.getSelection(),
            range = selection.getRangeAt(0),
            scrollIntoView = false,
            startPaste, // the first node to be pasted
            endPaste, // The last node to be pasted.
            newNode;
        if (editorHelpers.TEXT_ELEMENTS.indexOf(jQuery(range.startContainer).closest('.editable')[0].id)!=-1) {
            // We are inside an element that does not allow complex HTML.
            // Paste therefore only text contents and forget about footnotes.
            pasteElement.textContent = pasteElement.textContent;
            footnotes = false;
        }
        if (!pasteElement.firstChild) {
            // paste is empty.
            return true;
        }
        startPaste = pasteElement.firstChild;
        endPaste = pasteElement.lastChild;

        while (pasteElement.firstChild) {
            if (pasteElement.childNodes.length === 1) {
                scrollIntoView = true;
            }
            manualEdits.insert(pasteElement.firstChild, range, scrollIntoView);
        }
        if (startPaste.nodeName==='FIGURE' && ((!startPaste.previousSibling) || editorHelpers.TEXT_BLOCK_ELEMENTS.indexOf(startPaste.previousSibling.nodeName) === -1)) {
            // If there is no text block node before a figure at the start of the paste, add it now.
            newNode = document.createElement('p');
            newNode.innerHTML = '
';
            startPaste.parentNode.insertBefore(newNode, startPaste);
        } else if (startPaste.nodeName != 'FIGURE' && startPaste.previousSibling && editorHelpers.TEXT_BLOCK_ELEMENTS.indexOf(startPaste.previousSibling.nodeName) != -1 && startPaste.previousSibling.textContent.trim().length === 0) {
            // If there was an empty paragraph before the first pasted paragraph, remove it.
            startPaste.parentNode.removeChild(startPaste.previousSibling);
        }

        if (endPaste.nodeName==='FIGURE' && ((!endPaste.nextSibling) || editorHelpers.TEXT_BLOCK_ELEMENTS.indexOf(endPaste.nextSibling.nodeName) === -1)) {
            // If there is no text block node after a figure at the end of the paste, add it now.
            newNode = document.createElement('p');
            newNode.innerHTML = '
';
            endPaste.parentNode.insertBefore(newNode, endPaste.nextSibling);
        }
        if (footnotes) {
            editorEscapes.reset();
        }

        return true;
    };

    paste.insertText = function (textString) {
        var pasteElement = document.createElement('div');
            pasteElement.textContent = textString;
        paste.addPasteContents(pasteElement, false);
    };

    paste.insertHTML = function (htmlString) {
        var pasteElement = document.createElement('div'),
            htmlCleaner;
            pasteElement.innerHTML = htmlString;
            htmlCleaner = new cleanHTML(pasteElement);
        paste.addPasteContents(pasteElement, htmlCleaner.footnotes);
    };

    paste.recentlyPasted = false; // prevent double pasting by checking whether paste has been done recently

    paste.handlePaste = function(event) {
        // We cancel the paste event, copy clipboard data, clean it, insert it
        var htmlString;
        event.stopPropagation();
        event.preventDefault();
        if (!paste.recentlyPasted) {
            paste.recentlyPasted = true;
            setTimeout(function() {
                paste.recentlyPasted = false;
            }, 1)

            if (/text\/html/.test(event.clipboardData.types)) {
                paste.insertHTML(event.clipboardData.getData(
                    'text/html'));
            } else if (/text\/plain/.test(event.clipboardData.types)) {
                paste.insertText(event.clipboardData.getData(
                    'text/plain'));
            }
        }
    };

    exports.paste = paste;

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


<commit_before>/**
 * Bootstraps and starts the http server
 */

'use strict';

/**
 * Module dependencies
 * @private
 */
var connect = require('connect');
var http = require('http');
var morgan = require('morgan');
var qs = require('qs');

var parse = require('./parse');
var calculate = require('./calculate');

/**
 * Configure connect app and start http server
 */

module.exports = function() {
  var app = connect();

  // Log all incoming requests to the console
  app.use(morgan('common'));

  app.use('/calculate', function(req, res) {
    if(req.method === 'GET') {
      var params = qs.parse(req.url.substring(2));
      parse(params.expression, function(err, firstNum, operator, secondNum) {
        if(err) {
          res.statusCode = 422;
          return res.end(err);
        }

        var result = calculate(firstNum, operator, secondNum);
        res.end(result + '');
      });

    }
  });
  // Create server and listen on port 3000
  http.createServer(app).listen(3000);

  return app;
};
<commit_msg>Adding env var for port to listen on<commit_after>/**
 * Bootstraps and starts the http server
 */

'use strict';

/**
 * Module dependencies
 * @private
 */
var connect = require('connect');
var http = require('http');
var morgan = require('morgan');
var qs = require('qs');

var parse = require('./parse');
var calculate = require('./calculate');

/**
 * Configure connect app and start http server
 */

module.exports = function() {
  var app = connect();

  // Log all incoming requests to the console
  app.use(morgan('common'));

  app.use('/calculate', function(req, res) {
    if(req.method === 'GET') {
      var params = qs.parse(req.url.substring(2));
      parse(params.expression, function(err, firstNum, operator, secondNum) {
        if(err) {
          res.statusCode = 422;
          return res.end(err);
        }

        var result = calculate(firstNum, operator, secondNum);
        res.end(result + '');
      });

    }
  });
  // Create server and listen on port 3000
  http.createServer(app).listen(process.env.NODE_HTTP_PORT || 3000);

  return app;
};
<|endoftext|>


<commit_before>const Router = require('koa-router');
const logger = require('logger');
const TeamModel = require('models/team.model');
const TeamSerializer = require('serializers/team.serializer');
const TeamValidator = require('validators/team.validator');

const router = new Router({
    prefix: '/teams',
});

class TeamRouter {
  static async getById(ctx) {
      logger.info(`Getting team with id ${ctx.params.id}`);
      const team = await TeamModel.findById(ctx.params.id);
      ctx.body = TeamSerializer.serialize(team);
  }

  static async getByUserId(ctx) {
      logger.info(`Getting team for user with id ${ctx.params.userId}`);
      const team = await TeamModel.findOne({ users: ctx.params.userId });
      ctx.body = TeamSerializer.serialize(team);
  }

  static async create(ctx) {
      logger.info('Saving team', ctx.request.body);
      const body = ctx.request.body;
      const userId = ctx.request.body.loggedUser.id;
      if (typeof body.users !== 'undefined') body.users.push(userId);
      if (typeof body.managers !== 'undefined') body.managers.push(userId);
      const team = await new TeamModel({
          name: body.name,
          managers: body.managers,
          users: body.users,
          areas: body.areas,
          layers: body.layers,
          createdAt: Date.now()
      }).save();
      ctx.body = TeamSerializer.serialize(team);
  }

    static async update(ctx) {
        logger.info(`Updating team with id ${ctx.params.id}`);
        const parsedBody = ctx.request.body;
        const team = await TeamModel.findById(ctx.params.id);
        if (parsedBody.name) {
          team.name = parsedBody.name;
        }
        if (parsedBody.managers) {
          team.managers = parsedBody.managers;
        }
        if (parsedBody.users) {
        logger.info(`Users ${parsedBody.users}`);
          team.users = parsedBody.users;
        }
        if (parsedBody.areas) {
        logger.info(`Area ${parsedBody.areas}`);
          team.areas = parsedBody.areas;
        }
        if (parsedBody.layers) {
        logger.info(`Layer ${parsedBody.layers}`);
          team.layers = parsedBody.layers;
        }

        await team.save();
        ctx.body = TeamSerializer.serialize(team);
    }

    static async delete(ctx){
        logger.info(`Deleting team with id ${ctx.params.id}`);
        const result = await TeamModel.remove({ _id: ctx.params.id });
        if (!result || !result.result || result.result.ok === 0) {
            ctx.throw(404, 'Team not found');
            return;
        }
        ctx.body = '';
        ctx.statusCode = 204;
    }
}

router.get('/:id', TeamRouter.getById);
router.get('/user/:userId', TeamRouter.getByUserId);
router.post('/', TeamValidator.create, TeamRouter.create);
router.patch('/:id', TeamValidator.update, TeamRouter.update);
router.delete('/:id', TeamRouter.delete);

module.exports = router;
<commit_msg>Search for managers and confirmedUsers (and temporarily users)<commit_after>const Router = require('koa-router');
const logger = require('logger');
const TeamModel = require('models/team.model');
const TeamSerializer = require('serializers/team.serializer');
const TeamValidator = require('validators/team.validator');

const router = new Router({
    prefix: '/teams',
});

class TeamRouter {
  static async getById(ctx) {
      logger.info(`Getting team with id ${ctx.params.id}`);
      const team = await TeamModel.findById(ctx.params.id);
      ctx.body = TeamSerializer.serialize(team);
  }

  static async getByUserId(ctx) {
      logger.info(`Getting team for user with id ${ctx.params.userId}`);
      const team = await TeamModel.findOne({ managers: ctx.params.userId });
      if (!team){
        team = await TeamModel.findOne({ confirmedUsers: ctx.params.userId });
      }
      if (!team){                                                     // REMOVE THIS
        team = await TeamModel.findOne({ users: ctx.params.userId }); // REMOVE THIS
      }                                                               // REMOVE THIS
      ctx.body = TeamSerializer.serialize(team);
  }

  static async create(ctx) {
      logger.info('Saving team', ctx.request.body);
      const body = ctx.request.body;
      const userId = ctx.request.body.loggedUser.id;
      if (typeof body.users !== 'undefined') body.users.push(userId);
      if (typeof body.managers !== 'undefined') body.managers.push(userId);
      const team = await new TeamModel({
          name: body.name,
          managers: body.managers,
          users: body.users,
          areas: body.areas,
          layers: body.layers,
          createdAt: Date.now()
      }).save();
      ctx.body = TeamSerializer.serialize(team);
  }

    static async update(ctx) {
        logger.info(`Updating team with id ${ctx.params.id}`);
        const parsedBody = ctx.request.body;
        const team = await TeamModel.findById(ctx.params.id);
        if (parsedBody.name) {
          team.name = parsedBody.name;
        }
        if (parsedBody.managers) {
          team.managers = parsedBody.managers;
        }
        if (parsedBody.users) {
        logger.info(`Users ${parsedBody.users}`);
          team.users = parsedBody.users;
        }
        if (parsedBody.areas) {
        logger.info(`Area ${parsedBody.areas}`);
          team.areas = parsedBody.areas;
        }
        if (parsedBody.layers) {
        logger.info(`Layer ${parsedBody.layers}`);
          team.layers = parsedBody.layers;
        }

        await team.save();
        ctx.body = TeamSerializer.serialize(team);
    }

    static async delete(ctx){
        logger.info(`Deleting team with id ${ctx.params.id}`);
        const result = await TeamModel.remove({ _id: ctx.params.id });
        if (!result || !result.result || result.result.ok === 0) {
            ctx.throw(404, 'Team not found');
            return;
        }
        ctx.body = '';
        ctx.statusCode = 204;
    }
}

router.get('/:id', TeamRouter.getById);
router.get('/user/:userId', TeamRouter.getByUserId);
router.post('/', TeamValidator.create, TeamRouter.create);
router.patch('/:id', TeamValidator.update, TeamRouter.update);
router.delete('/:id', TeamRouter.delete);

module.exports = router;
<|endoftext|>


<commit_before>describe('app.tasks.add-sequence', function () {

  var accountId;

  beforeEach(function (done) {
    app.models.Account
      .create({
        name: 'hi',
        email: 'hi'
      }, function (err, account) {
        accountId = account._id;
        done();
      });
  });

  it('fails if the data-structure is not found', function (done) {
    app.tasks.addSequence({
      type: '4edd40c86762e0fb12000003'
    }, function (err, data) {
      assert.isNotNull(err);
      done();
    });
  });

  it('fails if the validation for the data fails', function (done) {
    app.models.DataStructure
      .create({
        validation: {
          type: 'object',
          properties: {
            age: {
              type: 'number'
            },
            name: {
              type: 'string'
            }
          }
        }
      }, function (err, struct) {
        app.tasks.addSequence({
          type: struct._id,
          data: {
            age: 'not a number',
            name: 9
          }
        }, function (err, data) {
          assert.isNotNull(err);
          done();
        });
      });
  });

  it('fails if the validations for the operations fail', function (done) {
    app.models.Operation
      .create({
        validation: { type: 'string' }
      }, {
        validation: { type: 'number' }
      }, function (err, op1, op2) {
        app.models.DataStructure
          .create({
            validation: { type: 'string' },
            operations: [ op1._id, op2._id ]
          }, function (err, struct) {
            app.tasks.addSequence({
              type: struct._id,
              data: 'string',
              operations: [
                { type: op1._id, data: 'string' },
                { type: op2._id, data: 'string' }
              ]
            }, function (err, data) {
              assert.isNotNull(err);
              done();
            });
          });
      });
  });

  it('creates a sequence otherwise', function (done) {
    app.models.Operation
      .create({
        validation: { type: 'string' }
      }, {
        validation: { type: 'number' }
      }, function (err, op1, op2) {
        app.models.DataStructure
          .create({
            validation: { type: 'string' },
            operations: [ op1._id, op2._id ]
          }, function (err, struct) {
            app.tasks.addSequence({
              type: struct._id,
              data: 'string',
              operations: [
                { type: op1._id, data: 'string' },
                { type: op2._id, data: 4 }
              ]
            }, function (err, data) {
              assert.isNull(err);

              app.models.Sequence
                .findOne({ _id: data._id })
                .exec(function (err, seq) {
                  assert.isNotNull(seq);
                  done();
                });
            });
          });
      });
  });
});<commit_msg>added a test for increased coverage<commit_after>describe('app.tasks.add-sequence', function () {

  var accountId;

  beforeEach(function (done) {
    app.models.Account
      .create({
        name: 'hi',
        email: 'hi'
      }, function (err, account) {
        accountId = account._id;
        done();
      });
  });

  it('fails if the data-structure is not found', function (done) {
    app.tasks.addSequence({
      type: '4edd40c86762e0fb12000003'
    }, function (err, data) {
      assert.isNotNull(err);
      done();
    });
  });

  it('fails if the validation for the data fails', function (done) {
    app.models.DataStructure
      .create({
        validation: {
          type: 'object',
          properties: {
            age: {
              type: 'number'
            },
            name: {
              type: 'string'
            }
          }
        }
      }, function (err, struct) {
        app.tasks.addSequence({
          type: struct._id,
          data: {
            age: 'not a number',
            name: 9
          }
        }, function (err, data) {
          assert.isNotNull(err);
          done();
        });
      });
  });

  it('fails if the validations for the operations fail', function (done) {
    app.models.Operation
      .create({
        validation: { type: 'string' }
      }, {
        validation: { type: 'number' }
      }, function (err, op1, op2) {
        app.models.DataStructure
          .create({
            validation: { type: 'string' },
            operations: [ op1._id, op2._id ]
          }, function (err, struct) {
            app.tasks.addSequence({
              type: struct._id,
              data: 'string',
              operations: [
                { type: op1._id, data: 'string' },
                { type: op2._id, data: 'string' }
              ]
            }, function (err, data) {
              assert.isNotNull(err);
              done();
            });
          });
      });
  });

  it('fails if the validation for the operation is not present', function (done) {
    app.models.DataStructure
      .create({
        validation: { type: 'string' },
        operations: []
      }, function (err, struct) {
        app.tasks.addSequence({
          type: struct._id,
          data: 'string',
          operations: [
            { type: '4edd40c86762e0fb12000003', data: 'string' }
          ]
        }, function (err, data) {
          assert.isNotNull(err);
          assert.equal(err, 'operation failed validation');
          done();
        });
      });
  });

  it('creates a sequence otherwise', function (done) {
    app.models.Operation
      .create({
        validation: { type: 'string' }
      }, {
        validation: { type: 'number' }
      }, function (err, op1, op2) {
        app.models.DataStructure
          .create({
            validation: { type: 'string' },
            operations: [ op1._id, op2._id ]
          }, function (err, struct) {
            app.tasks.addSequence({
              type: struct._id,
              data: 'string',
              operations: [
                { type: op1._id, data: 'string' },
                { type: op2._id, data: 4 }
              ]
            }, function (err, data) {
              assert.isNull(err);

              app.models.Sequence
                .findOne({ _id: data._id })
                .exec(function (err, seq) {
                  assert.isNotNull(seq);
                  done();
                });
            });
          });
      });
  });
});<|endoftext|>


<commit_before>import { ssrMiddleware } from 'quasar/wrappers'

// This middleware should execute as last one
// since it captures everything and tries to
// render the page with Vue

export default ssrMiddleware(({ app, resolve, render, serve }) => {
  // we capture any other Express route and hand it
  // over to Vue and Vue Router to render our page
  app.get(resolve.urlPath('*'), (req, res) => {
    res.setHeader('Content-Type', 'text/html')

    render({ req, res })
      .then(html => {
        // now let's send the rendered html to the client
        res.send(html)
      })
      .catch(err => {
        // oops, we had an error while rendering the page

        // we were told to redirect to another URL
        if (err.url) {
          if (err.code) {
            res.redirect(err.code, err.url)
          } else {
            res.redirect(err.url)
          }
        } else if (err.code === 404) {
          // hmm, Vue Router could not find the requested route

          // Should reach here only if no "catch-all" route
          // is defined in /src/routes
          res.status(404).send('404 | Page Not Found')
        } else if (process.env.DEV) {
          // well, we treat any other code as error;
          // if we're in dev mode, then we can use Quasar CLI
          // to display a nice error page that contains the stack
          // and other useful information

          // serve.error is available on dev only
          serve.error({ err, req, res })
        } else {
          // we're in production, so we should have another method
          // to display something to the client when we encounter an error
          // (for security reasons, it's not ok to display the same wealth
          // of information as we do in development)

          // Render Error Page on production or
          // create a route (/src/routes) for an error page and redirect to it
          res.status(500).send('500 | Internal Server Error')
        }
      })
  })
})
<commit_msg>chore(app): make the render ssr middleware a bit more complete<commit_after>import { ssrMiddleware } from 'quasar/wrappers'

// This middleware should execute as last one
// since it captures everything and tries to
// render the page with Vue

export default ssrMiddleware(({ app, resolve, render, serve }) => {
  // we capture any other Express route and hand it
  // over to Vue and Vue Router to render our page
  app.get(resolve.urlPath('*'), (req, res) => {
    res.setHeader('Content-Type', 'text/html')

    render(/* the ssrContext: */ { req, res })
      .then(html => {
        // now let's send the rendered html to the client
        res.send(html)
      })
      .catch(err => {
        // oops, we had an error while rendering the page

        // we were told to redirect to another URL
        if (err.url) {
          if (err.code) {
            res.redirect(err.code, err.url)
          } else {
            res.redirect(err.url)
          }
        } else if (err.code === 404) {
          // hmm, Vue Router could not find the requested route

          // Should reach here only if no "catch-all" route
          // is defined in /src/routes
          res.status(404).send('404 | Page Not Found')
        } else if (process.env.DEV) {
          // well, we treat any other code as error;
          // if we're in dev mode, then we can use Quasar CLI
          // to display a nice error page that contains the stack
          // and other useful information

          // serve.error is available on dev only
          serve.error({ err, req, res })
        } else {
          // we're in production, so we should have another method
          // to display something to the client when we encounter an error
          // (for security reasons, it's not ok to display the same wealth
          // of information as we do in development)

          // Render Error Page on production or
          // create a route (/src/routes) for an error page and redirect to it
          res.status(500).send('500 | Internal Server Error')
        }
      })
  })
})
<|endoftext|>


<commit_before>// Emit the necessary SAML SP metadata
//

console.log('Starting function - metadata');

var SAML = require('passport-saml').SAML;
var fs = require('fs');

exports.handler = function(event, context) {
    console.log('Received event:', JSON.stringify(event, null, 2));
    console.log('Got context: %j', context);

    var saml_options = {
        issuer: 'urn:rea:sshephalopod',
        validateInResponseTo: false, // turning this on requires an inmemorycache provider
        requestIdExpirationPeriodMs: 3600000,
        cacheProvider: {}, // since we won't be sticking around ...
        forceAuthn: true,
        identifierFormat: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
        decryptionPvk: fs.readFileSync("saml_sp.key").toString()
    };

    saml_options.callbackUrl =
        'https://' + event['api-id'] + '.execute-api.' + 'us-west-2' + '.amazonaws.com/' +
        event.stage + event['resource-path'];

    console.log('callbackURL is ' + saml_options.callbackUrl);

    var certificate = fs.readFileSync("saml_sp.crt").toString();

    var saml = new SAML(saml_options);

    var metadata = saml.generateServiceProviderMetadata(certificate);

    console.log("Created metadata: %j", metadata);
    context.done(null, metadata);
};
<commit_msg>Don't hardcode regions<commit_after>// Emit the necessary SAML SP metadata
//

console.log('Starting function - metadata');

var SAML = require('passport-saml').SAML;
var fs = require('fs');

exports.handler = function(event, context) {
    console.log('Received event:', JSON.stringify(event, null, 2));
    console.log('Got context: %j', context);

    var saml_options = {
        issuer: 'urn:rea:sshephalopod',
        validateInResponseTo: false, // turning this on requires an inmemorycache provider
        requestIdExpirationPeriodMs: 3600000,
        cacheProvider: {}, // since we won't be sticking around ...
        forceAuthn: true,
        identifierFormat: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
        decryptionPvk: fs.readFileSync("saml_sp.key").toString()
    };

    saml_options.callbackUrl =
        'https://' + event['api-id'] + '.execute-api.' + event.region + '.amazonaws.com/' +
        event.stage + event['resource-path'];

    console.log('callbackURL is ' + saml_options.callbackUrl);

    var certificate = fs.readFileSync("saml_sp.crt").toString();

    var saml = new SAML(saml_options);

    var metadata = saml.generateServiceProviderMetadata(certificate);

    console.log("Created metadata: %j", metadata);
    context.done(null, metadata);
};
<|endoftext|>


<commit_before>const Backbone = require('backbone');
const IconModel = require('./organization-icon-model');
const checkAndBuildOpts = require('builder/helpers/required-opts');

const REQUIRED_OPTS = [
  'orgId',
  'configModel'
];

module.exports = Backbone.Collection.extend({
  initialize: function (models, options) {
    checkAndBuildOpts(options, REQUIRED_OPTS, this);
  },

  model: function (attrs, opts) {
    const options = { ...opts, configModel: this._configModel };

    return new IconModel(attrs, options);
  },

  url: function (method) {
    const version = this._configModel.urlVersion('organization-assets', method);

    return `/api/${version}/organization/${this._orgId}/assets`;
  }
});
<commit_msg>add configModel properly<commit_after>const Backbone = require('backbone');
const IconModel = require('./organization-icon-model');
const checkAndBuildOpts = require('builder/helpers/required-opts');

const REQUIRED_OPTS = [
  'orgId',
  'configModel'
];

module.exports = Backbone.Collection.extend({
  initialize: function (models, options) {
    checkAndBuildOpts(options, REQUIRED_OPTS, this);
  },

  model: function (attrs, opts) {
    const options = { ...opts, configModel: opts.collection._configModel };

    return new IconModel(attrs, options);
  },

  url: function (method) {
    const version = this._configModel.urlVersion('organization-assets', method);

    return `/api/${version}/organization/${this._orgId}/assets`;
  }
});
<|endoftext|>


<commit_before>/*jshint node:true strict:false sub:true*/
// Dependencies
var parse = require('connect/lib/utils').parseUrl,
    fs = require('fs'),
    join = require('path').join;

// Export middleware factory
module.exports = createMiddleware;

/*
Options must contain the following keys:
  root: The root directory in which to open files
  match: A regular expression denoting which files to open
  transform: A function which takes three arguments.
    The first argument is the path to the file which was opened for transformation.
    The second argument is the text or buffer from the read file.
    The third argument is a callback function which accepts two arguments:
      The first argument is the transformed text / buffer. If this argument is set to `false` or `undefined` then the next connect middleware is invoked.
      The second argument (optional) is an object containing HTTP headers where keys (header names) map to values.
Optionally, the following keys may be specified:
  normalize: May be a string or a function.
    Example of string usage:
      match is set to /(.+)\.js/, normalize may be "$1.coffee".
      If the file "foo.js" is requested, then the file "foo.coffee" will be opened for transformation.
    Example of function usage:
      match is set to /.+\.js/, normalize may be function (name) { return name.substring(0, name.length - 2) + 'coffee' }
      If the file "foo.js" is requested, then the file "foo.coffee" will be opened for transformation.
  pathOnly: If true then the signature for the transformation function becomes `function (path, send) { }`, i.e., the file is not opened by `connect-static-transform`.
  cache: If true then the transformed text will be cached.
  maxage: Sets the maximum number of seconds a client should cache the output for. Defaults to one year.
  encoding: defaults to 'utf-8', may be set to "buffer" to indicate that the transformation function is expecting a buffer.
*/
function createMiddleware(options) {
  // Check for required options
  if (!(options.root || options.match || options.transform)) {
    throw new Error('options.{root,match,transform} must be defined');
  }

  // Set encoding if not set and set to undefined if set to 'buffer'
  var e = options.encoding;
  options.encoding = e ? (e === 'buffer' ? undefined : e) : 'utf-8';

  // Create cache object
  var cache = {};

  // Return middleware function
  return function (req, res, next) {
    // Check request method
    if (req.method !== 'GET' && req.method !== 'HEAD') {
      return next();
    }
    // Check request path
    var path = parse(req).pathname;
    if (!options.match.test(path)) {
      return next();
    }
    // Normalize path if necessary
    if (options.normalize) {
      if (typeof options.normalize === 'string') {
        path = path.replace(options.match, options.normalize);
      } else if (typeof options.normalize === 'function') {
        path = options.normalize(path);
      }
    }
    // Join path
    path = join(options.root, path);
    if (options.cache && cache[path]) {
      // Referenced cached value
      var cacheVal = cache[path];
      // Write cached value
      writeOut(req.headers['if-modified-since'] ? 304 : 200, res, cacheVal.out, cacheVal.headers, options);
    } else {
      // Stat the file
      fs.stat(path, function (err, stat) {
        if (err || !stat.isFile()) {
          return next();
        }
        // Pass path to transform if pathOnly is set
        if (options.pathOnly) {
          return options.transform(path, send);
        }
        // Read and transform file
        fs.readFile(path, options.encoding, function (err, data) {
          if (err) {
            throw err;
          }
          options.transform(path, data, send);
        });
        // Send function
        function send(out, headers) {
          // Check for headers
          if (!headers) {
            headers = {};
          }
          // Check out value
          if (!out) {
            return next();
          }
          // Do cache things if necessary
          if (options.cache) {
            var expire = new Date();
            expire.setYear(expire.getFullYear() + 1);
            headers['Expires'] = expire.toUTCString();
            headers['Cache-Control'] = 'public, max-age=' + (options.maxage || 31536000/*One year in seconds*/);
            headers['Last-Modified'] = stat.mtime.toUTCString();
            cache[path] = {out: out, headers: headers};
          }
          // Write out transformed value
          writeOut(200, res, out, headers, options);
        }
      });
    }
  };
}

// Write output
function writeOut(code, res, out, headers, options) {
  // Set length if necessary
  if (!headers['Content-Length']) {
    headers['Content-Length'] = out.length;
  }
  res.writeHead(code, headers);
  if (code === 200) {
    res.end(out, options.encoding);
  } else {
    res.end();
  }
}

// Export factory which creates middleware for Stylus
createMiddleware.stylus = (function () {
  // Require stylus in closure
  var stylus = null;
  try {
    stylus = require('stylus');
  } catch (e) {
    // Acts as factory and alerts user to install stylus
    return function () {
      throw e;
    };
  }
  // Attempt to grab nib
  var nib = null;
  try {
    nib = require('nib');
    nib = nib();
  } catch (e) {
    // nib not found, no big deal
    nib = function () {};
  }
  // Return factory function
  // This function is the value of module.exports.stylus
  return function (options) {
    // Return transformation middleware
    return createMiddleware({
      root: options.root,
      match: new RegExp((options.path || '') + '/(.+)\\.css'),
      normalize: '$1.styl',
      cache: options.cache,
      maxage: options.maxage,
      transform: function (path, styl, send) {
        stylus(styl)
          .set('filename', path)
          .set('compress', options.compress)
          .set('include css', true)
          .use(nib)
          .render(function (err, css) {
            if (err) {
              throw err;
            }
            // Stylus keeps newlines and inline comments, even when compressing.
            // Let's get rid of them if necessary
            if (options.compress) {
              css = css.replace(/\n/g, '');
              css = css.replace(/\/\*.+\*\//g, '');
            }
            // Send css to client
            send(css, {'Content-Type': 'text/css'});
          });
      }
    });
  };
})();

// Export factory which creates middleware for CoffeeScript
createMiddleware.coffee = (function () {
  // Require and instantiate Snockets
  var snockets = null;
  try {
    var Snockets = require('snockets');
    snockets = new Snockets();
  } catch (e) {
    // Acts as factory and alerts user to install Snockets
    return function () {
      throw e;
    };
  }
  // Return factory function
  // This function is the value of module.exports.coffee
  return function (options) {
    // Return transformation middleware
    return createMiddleware({
      root: options.root,
      match: new RegExp((options.path || '') + '/(.+)\\.js'),
      normalize: '$1.coffee',
      pathOnly: true,
      cache: options.cache,
      maxage: options.maxage,
      transform: function (path, send) {
        snockets.getConcatenation(path, options.options, function (err, js) {
          send(js, {'Content-Type': 'application/javascript'});
        });
      }
    });
  };
})();
<commit_msg>Calculates Content-Length in bytes instead of characters<commit_after>/*jshint node:true strict:false sub:true*/
// Dependencies
var parse = require('connect/lib/utils').parseUrl,
    fs = require('fs'),
    join = require('path').join;

// Export middleware factory
module.exports = createMiddleware;

/*
Options must contain the following keys:
  root: The root directory in which to open files
  match: A regular expression denoting which files to open
  transform: A function which takes three arguments.
    The first argument is the path to the file which was opened for transformation.
    The second argument is the text or buffer from the read file.
    The third argument is a callback function which accepts two arguments:
      The first argument is the transformed text / buffer. If this argument is set to `false` or `undefined` then the next connect middleware is invoked.
      The second argument (optional) is an object containing HTTP headers where keys (header names) map to values.
Optionally, the following keys may be specified:
  normalize: May be a string or a function.
    Example of string usage:
      match is set to /(.+)\.js/, normalize may be "$1.coffee".
      If the file "foo.js" is requested, then the file "foo.coffee" will be opened for transformation.
    Example of function usage:
      match is set to /.+\.js/, normalize may be function (name) { return name.substring(0, name.length - 2) + 'coffee' }
      If the file "foo.js" is requested, then the file "foo.coffee" will be opened for transformation.
  pathOnly: If true then the signature for the transformation function becomes `function (path, send) { }`, i.e., the file is not opened by `connect-static-transform`.
  cache: If true then the transformed text will be cached.
  maxage: Sets the maximum number of seconds a client should cache the output for. Defaults to one year.
  encoding: defaults to 'utf-8', may be set to "buffer" to indicate that the transformation function is expecting a buffer.
*/
function createMiddleware(options) {
  // Check for required options
  if (!(options.root || options.match || options.transform)) {
    throw new Error('options.{root,match,transform} must be defined');
  }

  // Set encoding if not set and set to undefined if set to 'buffer'
  var e = options.encoding;
  options.encoding = e ? (e === 'buffer' ? undefined : e) : 'utf-8';

  // Create cache object
  var cache = {};

  // Return middleware function
  return function (req, res, next) {
    // Check request method
    if (req.method !== 'GET' && req.method !== 'HEAD') {
      return next();
    }
    // Check request path
    var path = parse(req).pathname;
    if (!options.match.test(path)) {
      return next();
    }
    // Normalize path if necessary
    if (options.normalize) {
      if (typeof options.normalize === 'string') {
        path = path.replace(options.match, options.normalize);
      } else if (typeof options.normalize === 'function') {
        path = options.normalize(path);
      }
    }
    // Join path
    path = join(options.root, path);
    if (options.cache && cache[path]) {
      // Referenced cached value
      var cacheVal = cache[path];
      // Write cached value
      writeOut(req.headers['if-modified-since'] ? 304 : 200, res, cacheVal.out, cacheVal.headers, options);
    } else {
      // Stat the file
      fs.stat(path, function (err, stat) {
        if (err || !stat.isFile()) {
          return next();
        }
        // Pass path to transform if pathOnly is set
        if (options.pathOnly) {
          return options.transform(path, send);
        }
        // Read and transform file
        fs.readFile(path, options.encoding, function (err, data) {
          if (err) {
            throw err;
          }
          options.transform(path, data, send);
        });
        // Send function
        function send(out, headers) {
          // Check for headers
          if (!headers) {
            headers = {};
          }
          // Check out value
          if (!out) {
            return next();
          }
          // Do cache things if necessary
          if (options.cache) {
            var expire = new Date();
            expire.setYear(expire.getFullYear() + 1);
            headers['Expires'] = expire.toUTCString();
            headers['Cache-Control'] = 'public, max-age=' + (options.maxage || 31536000/*One year in seconds*/);
            headers['Last-Modified'] = stat.mtime.toUTCString();
            cache[path] = {out: out, headers: headers};
          }
          // Write out transformed value
          writeOut(200, res, out, headers, options);
        }
      });
    }
  };
}

// Write output
function writeOut(code, res, out, headers, options) {
  // Set length if necessary
  if (!headers['Content-Length']) {
    headers['Content-Length'] = Buffer.byteLength(out);
  }
  res.writeHead(code, headers);
  if (code === 200) {
    res.end(out, options.encoding);
  } else {
    res.end();
  }
}

// Export factory which creates middleware for Stylus
createMiddleware.stylus = (function () {
  // Require stylus in closure
  var stylus = null;
  try {
    stylus = require('stylus');
  } catch (e) {
    // Acts as factory and alerts user to install stylus
    return function () {
      throw e;
    };
  }
  // Attempt to grab nib
  var nib = null;
  try {
    nib = require('nib');
    nib = nib();
  } catch (e) {
    // nib not found, no big deal
    nib = function () {};
  }
  // Return factory function
  // This function is the value of module.exports.stylus
  return function (options) {
    // Return transformation middleware
    return createMiddleware({
      root: options.root,
      match: new RegExp((options.path || '') + '/(.+)\\.css'),
      normalize: '$1.styl',
      cache: options.cache,
      maxage: options.maxage,
      transform: function (path, styl, send) {
        stylus(styl)
          .set('filename', path)
          .set('compress', options.compress)
          .set('include css', true)
          .use(nib)
          .render(function (err, css) {
            if (err) {
              throw err;
            }
            // Stylus keeps newlines and inline comments, even when compressing.
            // Let's get rid of them if necessary
            if (options.compress) {
              css = css.replace(/\n/g, '');
              css = css.replace(/\/\*.+\*\//g, '');
            }
            // Send css to client
            send(css, {'Content-Type': 'text/css'});
          });
      }
    });
  };
})();

// Export factory which creates middleware for CoffeeScript
createMiddleware.coffee = (function () {
  // Require and instantiate Snockets
  var snockets = null;
  try {
    var Snockets = require('snockets');
    snockets = new Snockets();
  } catch (e) {
    // Acts as factory and alerts user to install Snockets
    return function () {
      throw e;
    };
  }
  // Return factory function
  // This function is the value of module.exports.coffee
  return function (options) {
    // Return transformation middleware
    return createMiddleware({
      root: options.root,
      match: new RegExp((options.path || '') + '/(.+)\\.js'),
      normalize: '$1.coffee',
      pathOnly: true,
      cache: options.cache,
      maxage: options.maxage,
      transform: function (path, send) {
        snockets.getConcatenation(path, options.options, function (err, js) {
          send(js, {'Content-Type': 'application/javascript'});
        });
      }
    });
  };
})();
<|endoftext|>


<commit_before>var decapitalize = require('./decapitalize');
var toString = require('./toString');

module.exports = function camelize(str, capitalize) {
    string = toString(string);
    string = string.replace(/[-_\s]+(.)?/g, function(match, c) {
        return c ? c.toUpperCase() : "";
    });

    if (capitalize === true) {
        return string;
    } else {
        return decapitalize(string);
    }
};
<commit_msg>Update camelize.js<commit_after>var decapitalize = require('./decapitalize');
var toString = require('./toString');

/**
 * @param {string} string
 * @param {boolean} [capitalize=false]
 */
module.exports = function camelize(string, capitalize) {
    string = toString(string);
    string = string.replace(/[-_\s]+(.)?/g, function(match, c) {
        return c ? c.toUpperCase() : "";
    });

    if (capitalize === true) {
        return string;
    } else {
        return decapitalize(string);
    }
};
<|endoftext|>


<commit_before>"use strict";

const Base = require("./Base");
const Endpoints = require("../rest/Endpoints");

/**
* Represents a user
* @prop {String} id The ID of the user
* @prop {Number} createdAt Timestamp of the user's creation
* @prop {String} mention A string that mentions the user
* @prop {String} defaultAvatar The hash for the default avatar of a user if there is no avatar set
* @prop {Number} createdAt Timestamp of user creation
* @prop {Boolean} bot Whether the user is an OAuth bot or not
* @prop {String} username The username of the user
* @prop {String} discriminator The discriminator of the user
* @prop {String?} avatar The hash of the user's avatar, or null if no avatar
* @prop {String} defaultAvatarURL The URL of the user's default avatar
* @prop {String} avatarURL The URL of the user's avatar which can be either a JPG or GIF
* @prop {String} staticAvatarURL The URL of the user's avatar (always a JPG)
* @prop {Boolean} system Whether the user is an official Discord system user (e.g. urgent messages)
*/
class User extends Base {
    constructor(data, client) {
        super(data.id);
        if(!client) {
            this._missingClientError = new Error("Missing client in constructor"); // Preserve constructor callstack
        }
        this._client = client;
        this.bot = !!data.bot;
        this.system = !!data.system;
        this.update(data);
    }

    update(data) {
        if(data.avatar !== undefined) {
            this.avatar = data.avatar;
        }
        if(data.username !== undefined) {
            this.username = data.username;
        }
        if(data.discriminator !== undefined) {
            this.discriminator = data.discriminator;
        }
    }

    get mention() {
        return `<@${this.id}>`;
    }

    get defaultAvatar() {
        return this.discriminator % 5;
    }

    get defaultAvatarURL() {
        return `${Endpoints.CDN_URL}${Endpoints.DEFAULT_USER_AVATAR(this.defaultAvatar)}.png`;
    }

    get staticAvatarURL() {
        if(this._missingClientError) {
            throw this._missingClientError;
        }
        return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), "jpg") : this.defaultAvatarURL;
    }

    get avatarURL() {
        if(this._missingClientError) {
            throw this._missingClientError;
        }
        return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar)) : null;
    }

    /**
    * Get the user's avatar with the given format and size
    * @arg {String} [format] The filetype of the avatar ("jpg", "png", "gif", or "webp")
    * @arg {Number} [size] The size of the avatar (any power of two between 16 and 2048)
    */
    dynamicAvatarURL(format, size) {
        return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), format, size) : this.defaultAvatarURL;
    }

    /**
    * Get a DM channel with the user, or create one if it does not exist
    * @returns {Promise<PrivateChannel>}
    */
    getDMChannel() {
        return this._client.getDMChannel.call(this._client, this.id);
    }

    /**
    * Create a relationship with the user (user accounts only)
    * @arg {Boolean} [block=false] If true, block the user. Otherwise, add the user as a friend
    * @returns {Promise}
    */
    addRelationship(block) {
        return this._client.addRelationship.call(this._client, this.id, block);
    }

    /**
    * Remove a relationship with the user (user accounts only)
    * @returns {Promise}
    */
    removeRelationship() {
        return this._client.removeRelationship.call(this._client, this.id);
    }

    /**
    * Get profile data for the user (user accounts only)
    * @returns {Promise<Object>} The user's profile data.
    */
    getProfile() {
        return this._client.getUserProfile.call(this._client, this.id);
    }

    /**
    * Edit the current user's note for the user (user accounts only)
    * @arg {String} note The note
    * @returns {Promise}
    */
    editNote(note) {
        return this._client.editUserNote.call(this._client, this.id, note);
    }

    /**
    * Delete the current user's note for another user (user accounts only)
    */
    deleteNote() {
        return this._client.deleteUserNote.call(this._client, this.id);
    }

    toJSON(props = []) {
        return super.toJSON([
            "avatar",
            "bot",
            "discriminator",
            "username",
            ...props
        ]);
    }
}

module.exports = User;
<commit_msg>Fix User#avatarURL fallback (#637)<commit_after>"use strict";

const Base = require("./Base");
const Endpoints = require("../rest/Endpoints");

/**
* Represents a user
* @prop {String} id The ID of the user
* @prop {Number} createdAt Timestamp of the user's creation
* @prop {String} mention A string that mentions the user
* @prop {String} defaultAvatar The hash for the default avatar of a user if there is no avatar set
* @prop {Number} createdAt Timestamp of user creation
* @prop {Boolean} bot Whether the user is an OAuth bot or not
* @prop {String} username The username of the user
* @prop {String} discriminator The discriminator of the user
* @prop {String?} avatar The hash of the user's avatar, or null if no avatar
* @prop {String} defaultAvatarURL The URL of the user's default avatar
* @prop {String} avatarURL The URL of the user's avatar which can be either a JPG or GIF
* @prop {String} staticAvatarURL The URL of the user's avatar (always a JPG)
* @prop {Boolean} system Whether the user is an official Discord system user (e.g. urgent messages)
*/
class User extends Base {
    constructor(data, client) {
        super(data.id);
        if(!client) {
            this._missingClientError = new Error("Missing client in constructor"); // Preserve constructor callstack
        }
        this._client = client;
        this.bot = !!data.bot;
        this.system = !!data.system;
        this.update(data);
    }

    update(data) {
        if(data.avatar !== undefined) {
            this.avatar = data.avatar;
        }
        if(data.username !== undefined) {
            this.username = data.username;
        }
        if(data.discriminator !== undefined) {
            this.discriminator = data.discriminator;
        }
    }

    get mention() {
        return `<@${this.id}>`;
    }

    get defaultAvatar() {
        return this.discriminator % 5;
    }

    get defaultAvatarURL() {
        return `${Endpoints.CDN_URL}${Endpoints.DEFAULT_USER_AVATAR(this.defaultAvatar)}.png`;
    }

    get staticAvatarURL() {
        if(this._missingClientError) {
            throw this._missingClientError;
        }
        return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), "jpg") : this.defaultAvatarURL;
    }

    get avatarURL() {
        if(this._missingClientError) {
            throw this._missingClientError;
        }
        return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar)) : this.defaultAvatarURL;
    }

    /**
    * Get the user's avatar with the given format and size
    * @arg {String} [format] The filetype of the avatar ("jpg", "png", "gif", or "webp")
    * @arg {Number} [size] The size of the avatar (any power of two between 16 and 2048)
    */
    dynamicAvatarURL(format, size) {
        return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), format, size) : this.defaultAvatarURL;
    }

    /**
    * Get a DM channel with the user, or create one if it does not exist
    * @returns {Promise<PrivateChannel>}
    */
    getDMChannel() {
        return this._client.getDMChannel.call(this._client, this.id);
    }

    /**
    * Create a relationship with the user (user accounts only)
    * @arg {Boolean} [block=false] If true, block the user. Otherwise, add the user as a friend
    * @returns {Promise}
    */
    addRelationship(block) {
        return this._client.addRelationship.call(this._client, this.id, block);
    }

    /**
    * Remove a relationship with the user (user accounts only)
    * @returns {Promise}
    */
    removeRelationship() {
        return this._client.removeRelationship.call(this._client, this.id);
    }

    /**
    * Get profile data for the user (user accounts only)
    * @returns {Promise<Object>} The user's profile data.
    */
    getProfile() {
        return this._client.getUserProfile.call(this._client, this.id);
    }

    /**
    * Edit the current user's note for the user (user accounts only)
    * @arg {String} note The note
    * @returns {Promise}
    */
    editNote(note) {
        return this._client.editUserNote.call(this._client, this.id, note);
    }

    /**
    * Delete the current user's note for another user (user accounts only)
    */
    deleteNote() {
        return this._client.deleteUserNote.call(this._client, this.id);
    }

    toJSON(props = []) {
        return super.toJSON([
            "avatar",
            "bot",
            "discriminator",
            "username",
            ...props
        ]);
    }
}

module.exports = User;
<|endoftext|>


<commit_before>const colors = require('colors')
const _ = require('lodash')

function Omissions(root){
  if(!root || typeof root !== 'string'){
    throw new Error('Ommisions constructor requires a root property of type String')
  }
  this.root = root
  this.omissions = new Array(0)
  this.props = new Array(0)
}

Omissions.prototype.add = function (omission) {
  this.omissions.push(omission)
}

Omissions.prototype.addProp = function (prop) {
  this.props.push(prop)
}

Omissions.prototype.logOmissions = function() {
  let omString = ''
  _.forEach(this.omissions, (omission) => {
    omString += `   -- ${omission} \n`
  })
  console.log(
    `  ${String(this.root).red} \n${String(omString).yellow}`
  )
}

module.exports = Omissions
<commit_msg>- adding props which need to be further inspected<commit_after>const colors = require('colors')
const _ = require('lodash')

function Omissions(root){
  if(!root || typeof root !== 'string'){
    throw new Error('Ommisions constructor requires a root property of type String')
  }
  this.root = root
  this.omissions = new Array(0)
  this.props = new Array(0)
  this.inspectProps = new Array(0)
}

Omissions.prototype.add = function (omission) {
  this.omissions.push(omission)
}

Omissions.prototype.addProp = function (prop) {
  this.props.push(prop)
}

Omissions.proptype.addInspectProp = function (prop) {
  this.inspectProps.push(prop)
}

Omissions.prototype.logOmissions = function() {
  let omString = ''
  _.forEach(this.omissions, (omission) => {
    omString += `   -- ${omission} \n`
  })
  console.log(
    `  ${String(this.root).red} \n${String(omString).yellow}`
  )
}

module.exports = Omissions
<|endoftext|>