<commit_before>import sls from 'single-line-string';

import { cartoTables } from './app_config';

const { nyc_borough,
  nyc_city_council,
  nyc_community_board,
  nyc_neighborhood,
  nyc_nypd_precinct,
  nyc_zip_code,
  nyc_crashes } = cartoTables;

// Links each boundary filter name to a SQL query
export const filterByAreaSQL = {
  Borough: sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_borough}
    ORDER BY
      identifier
  `,

  'City Council District': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_city_council}
    ORDER BY
      identifier
  `,

  'Community Board': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_community_board}
    ORDER BY
      identifier
  `,

  'Neighborhood (NTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_neighborhood}
    ORDER BY
      borough, identifier
  `,

  'NYPD Precinct': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_nypd_precinct}
    ORDER BY
      borough, identifier
  `,

  'Zipcode (ZCTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_zip_code}
    ORDER BY
      borough, identifier
  `,
};

// Selects max and min date from all crashes
export const crashesYearRangeSQL = () => sls`
  SELECT DISTINCT year
  FROM ${nyc_crashes}
  ORDER BY year DESC
`;

export const minMaxDateRange = () => sls`
  SELECT
  min(year::text || '-' || LPAD(month::text, 2, '0')),
  max(year::text || '-' || LPAD(month::text, 2, '0'))
  FROM ${nyc_crashes}
`;

/*
***************************** SQL HELPERS **************************************
*/

// Generates the SQL WHERE clause for "Filter by Date Range"
// @param {object} startDate, a moment.js object
// @param {object} endDate, a moment.js object
const filterByDateWhereClause = (startDate, endDate) =>
  sls`
      (
        year::text || LPAD(month::text, 2, '0') <=
        '${endDate.year()}' || LPAD(${endDate.month() + 1}::text, 2, '0')
      )
    AND
      (
        year::text || LPAD(month::text, 2, '0') >=
        '${startDate.year()}' || LPAD(${startDate.month() + 1}::text, 2, '0')
      )
  `;

// Generates the SQL WHERE clause for "Filter by Type"
// @param {object} the store.filterType piece of state
const filterByTypeWhereClause = (filterType) => {
  const { injury, fatality, noInjuryFatality } = filterType;
  let whereClause = '';

  const mapTypes = (personTypes, harmType) =>
    Object.keys(personTypes).filter((type) => {
      const val = personTypes[type];
      if (val) return type;
      return false;
    })
    .map((type) => {
      const hurtTerm = harmType === 'injury' ? 'injured' : 'killed';
      return ` number_of_${type}_${hurtTerm} > 0 `;
    })
    .join('OR');

  const typesInjuredMapped = mapTypes(injury, 'injury');
  const typesKilledMapped = mapTypes(fatality, 'fatality');

  if (typesInjuredMapped.length > 0 && typesKilledMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped} OR ${typesKilledMapped})`;
  } else if (typesInjuredMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped})`;
  } else if (typesKilledMapped.length > 0) {
    whereClause += `AND (${typesKilledMapped})`;
  } else if (noInjuryFatality) {
    whereClause += sls`AND
      number_of_cyclist_injured = 0 AND
      number_of_cyclist_killed = 0 AND
      number_of_motorist_injured = 0 AND
      number_of_motorist_killed = 0 AND
      number_of_pedestrian_injured = 0 AND
      number_of_pedestrian_killed = 0 AND
      number_of_persons_injured = 0 AND
      number_of_persons_killed = 0
    `;
  }

  return whereClause;
};

// Links the Filter by Boundary button name to corresponding Carto table name
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
export const filterAreaBtnTableMap = {
  Borough: undefined,
  'Community Board': nyc_community_board,
  'City Council District': nyc_city_council,
  'Neighborhood (NTA)': nyc_neighborhood,
  'NYPD Precinct': nyc_nypd_precinct,
  'Zipcode (ZCTA)': nyc_zip_code,
};

// Creates the spatial join clause with a boundary table geom
// @param {string} areaName, name of boundary, eg 'Borough' or 'City Council Districts'
const joinToGeoTableClause = (areaName) => {
  const geoTable = filterAreaBtnTableMap[areaName];
  if (geoTable) {
    return sls`
      JOIN ${geoTable} a
      ON ST_Within(c.the_geom, a.the_geom)
    `;
  }
  return '';
};

// name mappings for Carto table nyc_borough "identifier" to borough name
const boroughs = ['manhattan', 'bronx', 'brooklyn', 'queens', 'staten island'];

// Creates the WHERE clause for boundary table identifier
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
// @param {number || string} identifier, unique id of boundary polygon
// @param {string} geo, name of boundary table identifier column belongs to
const filterByIdentifierWhereClause = (identifier, geo) => {
  if (geo !== 'Borough' && identifier) {
    return `AND a.identifier = $$${identifier}$$`;
  } else if (geo === 'Borough' && identifier) {
    return `AND upper(c.borough) = upper('${boroughs[identifier - 1]}')`;
  }
  return '';
};

// Creates the PostGIS query for selecting crash data by custom area created by Leaflet.Draw
// @param {array} lonLatArray, an array of longitude, latitude arrays that form an enclosed polygon
const filterByCustomAreaClause = (lonLatArray) => {
  if (lonLatArray && lonLatArray.length) {
    // PostGIS GeomFromText expects lon lat coords like (-73.91 40.74, -73.89 40.73, ...)
    const coordinates = lonLatArray.map(lonLat => lonLat.join(' '));
    return sls`
      AND
        ST_Contains(
          ST_GeomFromText(
            'POLYGON(( ${coordinates} ))',
          4326),
          c.the_geom
        )
    `;
  }
  return '';
};

/*
 ********************************** MAP ****************************************
 */

// Generates the SQL query for the Carto layer based on filter params & app element
// @param {object} params: key values associated with filters derived from app state
// @param {string} startDate: min date; required
// @param {string} endDate: max date; required
// @param {string} harm: crash type, one of 'ALL', 'cyclist', 'motorist', 'ped'
// @param {string} persona: crash type, of of 'ALL', 'fatality', 'injury', 'no inj/fat'
export const configureMapSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT * FROM
    (
      SELECT
        c.the_geom as the_geom,
        c.the_geom_webmercator as the_geom_webmercator,
        c.on_street_name as on_street_name,
        c.cross_street_name as cross_street_name,
        COUNT(c.crash_count) as total_crashes,
        SUM(c.number_of_cyclist_injured) as cyclist_injured,
        SUM(c.number_of_cyclist_killed) as cyclist_killed,
        SUM(c.number_of_motorist_injured) as motorist_injured,
        SUM(c.number_of_motorist_killed) as motorist_killed,
        SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
        SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
        SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
        SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
      FROM
        ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
      AND
        c.the_geom IS NOT NULL
      GROUP BY
        c.the_geom, c.the_geom_webmercator, c.on_street_name, c.cross_street_name
    ) _
    ORDER BY
    CASE WHEN (persons_killed > 0) THEN 3
    WHEN (persons_injured > 0) THEN 2
    WHEN (total_crashes > 11) THEN 1
    ELSE 0
    END
  `;
};

/*
 ******************************* STATS *****************************************
 */

export const configureStatsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT
      COUNT(c.cartodb_id) as total_crashes,
      SUM(c.number_of_cyclist_injured) as cyclist_injured,
      SUM(c.number_of_cyclist_killed) as cyclist_killed,
      SUM(c.number_of_motorist_injured) as motorist_injured,
      SUM(c.number_of_motorist_killed) as motorist_killed,
      SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
      SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
      SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
      SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
    FROM
      ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier, geo)}
  `;
};

/*
 *************************** CONTRIBUTING FACTORS ******************************
*/

export const configureFactorsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    WITH all_factors as (
      SELECT
        unnest(c.contributing_factor) as factor
      FROM
      ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
    )
    SELECT
     COUNT(af.factor) as count_factor,
     af.factor
    FROM
      all_factors af
    GROUP BY
      af.factor
    ORDER BY
      count_factor desc
  `;
};

/*
 ******************************* Download Data *********************************
*/

// Creates the SQL query for "Download Data" buttons
export const configureDownloadDataSQL = (params) => {
  const { startDate, endDate, filterArea, filterType } = params;
  const { geo, lngLats, identifier } = filterArea;

  return sls`
    SELECT
      c.cartodb_id,
      c.unique_key,
      c.the_geom,
      c.on_street_name,
      c.cross_street_name,
      c.date_val AS date,
      c.latitude,
      c.longitude,
      c.borough,
      c.zip_code,
      c.crash_count,
      c.number_of_cyclist_injured,
      c.number_of_cyclist_killed,
      c.number_of_motorist_injured,
      c.number_of_motorist_killed,
      c.number_of_pedestrian_injured,
      c.number_of_pedestrian_killed,
      c.number_of_persons_injured,
      c.number_of_persons_killed,
      c.contributing_factor,
      c.vehicle_type
    FROM ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier)}
  `;
};
<commit_msg>Match on borough using ilike operator #51<commit_after>import sls from 'single-line-string';

import { cartoTables } from './app_config';

const { nyc_borough,
  nyc_city_council,
  nyc_community_board,
  nyc_neighborhood,
  nyc_nypd_precinct,
  nyc_zip_code,
  nyc_crashes } = cartoTables;

// Links each boundary filter name to a SQL query
export const filterByAreaSQL = {
  Borough: sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_borough}
    ORDER BY
      identifier
  `,

  'City Council District': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_city_council}
    ORDER BY
      identifier
  `,

  'Community Board': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_community_board}
    ORDER BY
      identifier
  `,

  'Neighborhood (NTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_neighborhood}
    ORDER BY
      borough, identifier
  `,

  'NYPD Precinct': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_nypd_precinct}
    ORDER BY
      borough, identifier
  `,

  'Zipcode (ZCTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_zip_code}
    ORDER BY
      borough, identifier
  `,
};

// Selects max and min date from all crashes
export const crashesYearRangeSQL = () => sls`
  SELECT DISTINCT year
  FROM ${nyc_crashes}
  ORDER BY year DESC
`;

export const minMaxDateRange = () => sls`
  SELECT
  min(year::text || '-' || LPAD(month::text, 2, '0')),
  max(year::text || '-' || LPAD(month::text, 2, '0'))
  FROM ${nyc_crashes}
`;

/*
***************************** SQL HELPERS **************************************
*/

// Generates the SQL WHERE clause for "Filter by Date Range"
// @param {object} startDate, a moment.js object
// @param {object} endDate, a moment.js object
const filterByDateWhereClause = (startDate, endDate) =>
  sls`
      (
        year::text || LPAD(month::text, 2, '0') <=
        '${endDate.year()}' || LPAD(${endDate.month() + 1}::text, 2, '0')
      )
    AND
      (
        year::text || LPAD(month::text, 2, '0') >=
        '${startDate.year()}' || LPAD(${startDate.month() + 1}::text, 2, '0')
      )
  `;

// Generates the SQL WHERE clause for "Filter by Type"
// @param {object} the store.filterType piece of state
const filterByTypeWhereClause = (filterType) => {
  const { injury, fatality, noInjuryFatality } = filterType;
  let whereClause = '';

  const mapTypes = (personTypes, harmType) =>
    Object.keys(personTypes).filter((type) => {
      const val = personTypes[type];
      if (val) return type;
      return false;
    })
    .map((type) => {
      const hurtTerm = harmType === 'injury' ? 'injured' : 'killed';
      return ` number_of_${type}_${hurtTerm} > 0 `;
    })
    .join('OR');

  const typesInjuredMapped = mapTypes(injury, 'injury');
  const typesKilledMapped = mapTypes(fatality, 'fatality');

  if (typesInjuredMapped.length > 0 && typesKilledMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped} OR ${typesKilledMapped})`;
  } else if (typesInjuredMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped})`;
  } else if (typesKilledMapped.length > 0) {
    whereClause += `AND (${typesKilledMapped})`;
  } else if (noInjuryFatality) {
    whereClause += sls`AND
      number_of_cyclist_injured = 0 AND
      number_of_cyclist_killed = 0 AND
      number_of_motorist_injured = 0 AND
      number_of_motorist_killed = 0 AND
      number_of_pedestrian_injured = 0 AND
      number_of_pedestrian_killed = 0 AND
      number_of_persons_injured = 0 AND
      number_of_persons_killed = 0
    `;
  }

  return whereClause;
};

// Links the Filter by Boundary button name to corresponding Carto table name
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
export const filterAreaBtnTableMap = {
  Borough: undefined,
  'Community Board': nyc_community_board,
  'City Council District': nyc_city_council,
  'Neighborhood (NTA)': nyc_neighborhood,
  'NYPD Precinct': nyc_nypd_precinct,
  'Zipcode (ZCTA)': nyc_zip_code,
};

// Creates the spatial join clause with a boundary table geom
// @param {string} areaName, name of boundary, eg 'Borough' or 'City Council Districts'
const joinToGeoTableClause = (areaName) => {
  const geoTable = filterAreaBtnTableMap[areaName];
  if (geoTable) {
    return sls`
      JOIN ${geoTable} a
      ON ST_Within(c.the_geom, a.the_geom)
    `;
  }
  return '';
};

// name mappings for Carto table nyc_borough "identifier" to borough name
const boroughs = ['manhattan', 'bronx', 'brooklyn', 'queens', 'staten island'];

// Creates the WHERE clause for boundary table identifier
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
// @param {number || string} identifier, unique id of boundary polygon
// @param {string} geo, name of boundary table identifier column belongs to
const filterByIdentifierWhereClause = (identifier, geo) => {
  if (geo !== 'Borough' && identifier) {
    return `AND a.identifier = $$${identifier}$$`;
  } else if (geo === 'Borough' && identifier) {
    return `AND c.borough ilike '%${boroughs[identifier - 1]}%'`;
  }
  return '';
};

// Creates the PostGIS query for selecting crash data by custom area created by Leaflet.Draw
// @param {array} lonLatArray, an array of longitude, latitude arrays that form an enclosed polygon
const filterByCustomAreaClause = (lonLatArray) => {
  if (lonLatArray && lonLatArray.length) {
    // PostGIS GeomFromText expects lon lat coords like (-73.91 40.74, -73.89 40.73, ...)
    const coordinates = lonLatArray.map(lonLat => lonLat.join(' '));
    return sls`
      AND
        ST_Contains(
          ST_GeomFromText(
            'POLYGON(( ${coordinates} ))',
          4326),
          c.the_geom
        )
    `;
  }
  return '';
};

/*
 ********************************** MAP ****************************************
 */

// Generates the SQL query for the Carto layer based on filter params & app element
// @param {object} params: key values associated with filters derived from app state
// @param {string} startDate: min date; required
// @param {string} endDate: max date; required
// @param {string} harm: crash type, one of 'ALL', 'cyclist', 'motorist', 'ped'
// @param {string} persona: crash type, of of 'ALL', 'fatality', 'injury', 'no inj/fat'
export const configureMapSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT * FROM
    (
      SELECT
        c.the_geom as the_geom,
        c.the_geom_webmercator as the_geom_webmercator,
        c.on_street_name as on_street_name,
        c.cross_street_name as cross_street_name,
        COUNT(c.crash_count) as total_crashes,
        SUM(c.number_of_cyclist_injured) as cyclist_injured,
        SUM(c.number_of_cyclist_killed) as cyclist_killed,
        SUM(c.number_of_motorist_injured) as motorist_injured,
        SUM(c.number_of_motorist_killed) as motorist_killed,
        SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
        SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
        SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
        SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
      FROM
        ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
      AND
        c.the_geom IS NOT NULL
      GROUP BY
        c.the_geom, c.the_geom_webmercator, c.on_street_name, c.cross_street_name
    ) _
    ORDER BY
    CASE WHEN (persons_killed > 0) THEN 3
    WHEN (persons_injured > 0) THEN 2
    WHEN (total_crashes > 11) THEN 1
    ELSE 0
    END
  `;
};

/*
 ******************************* STATS *****************************************
 */

export const configureStatsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT
      COUNT(c.cartodb_id) as total_crashes,
      SUM(c.number_of_cyclist_injured) as cyclist_injured,
      SUM(c.number_of_cyclist_killed) as cyclist_killed,
      SUM(c.number_of_motorist_injured) as motorist_injured,
      SUM(c.number_of_motorist_killed) as motorist_killed,
      SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
      SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
      SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
      SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
    FROM
      ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier, geo)}
  `;
};

/*
 *************************** CONTRIBUTING FACTORS ******************************
*/

export const configureFactorsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    WITH all_factors as (
      SELECT
        unnest(c.contributing_factor) as factor
      FROM
      ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
    )
    SELECT
     COUNT(af.factor) as count_factor,
     af.factor
    FROM
      all_factors af
    GROUP BY
      af.factor
    ORDER BY
      count_factor desc
  `;
};

/*
 ******************************* Download Data *********************************
*/

// Creates the SQL query for "Download Data" buttons
export const configureDownloadDataSQL = (params) => {
  const { startDate, endDate, filterArea, filterType } = params;
  const { geo, lngLats, identifier } = filterArea;

  return sls`
    SELECT
      c.cartodb_id,
      c.unique_key,
      c.the_geom,
      c.on_street_name,
      c.cross_street_name,
      c.date_val AS date,
      c.latitude,
      c.longitude,
      c.borough,
      c.zip_code,
      c.crash_count,
      c.number_of_cyclist_injured,
      c.number_of_cyclist_killed,
      c.number_of_motorist_injured,
      c.number_of_motorist_killed,
      c.number_of_pedestrian_injured,
      c.number_of_pedestrian_killed,
      c.number_of_persons_injured,
      c.number_of_persons_killed,
      c.contributing_factor,
      c.vehicle_type
    FROM ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier)}
  `;
};
<|endoftext|>


<commit_before>import sls from 'single-line-string';

import { cartoTables } from './app_config';

const { nyc_borough,
  nyc_city_council,
  nyc_community_board,
  nyc_neighborhood,
  nyc_nypd_precinct,
  nyc_zip_code,
  nyc_crashes } = cartoTables;

// Links each boundary filter name to a SQL query
export const filterByAreaSQL = {
  Borough: sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_borough}
    ORDER BY
      identifier
  `,

  'City Council District': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_city_council}
    ORDER BY
      identifier
  `,

  'Community Board': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_community_board}
    ORDER BY
      identifier
  `,

  'Neighborhood (NTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_neighborhood}
    ORDER BY
      borough, identifier
  `,

  'NYPD Precinct': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_nypd_precinct}
    ORDER BY
      borough, identifier
  `,

  'Zipcode (ZCTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_zip_code}
    ORDER BY
      borough, identifier
  `,
};

// Selects max and min date from all crashes
export const dateBounds = () => sls`
  SELECT
    MAX(date) as max_date,
    MIN(date) as min_date
  FROM
    ${nyc_crashes}
`;

/*
***************************** SQL HELPERS **************************************
*/

// Generates the SQL WHERE clause for "Filter by Type"
// @param {object} the store.filterType piece of state
const filterByTypeWhereClause = (filterType) => {
  const { injury, fatality, noInjuryFatality } = filterType;
  let whereClause = '';

  const mapTypes = (personTypes, harmType) =>
    Object.keys(personTypes).filter((type) => {
      const val = personTypes[type];
      if (val) return type;
      return false;
    })
    .map((type) => {
      const hurtTerm = harmType === 'injury' ? 'injured' : 'killed';
      return ` number_of_${type}_${hurtTerm} > 0 `;
    })
    .join('OR');

  const typesInjuredMapped = mapTypes(injury, 'injury');
  const typesKilledMapped = mapTypes(fatality, 'fatality');

  if (typesInjuredMapped.length > 0 && typesKilledMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped} OR ${typesKilledMapped})`;
  } else if (typesInjuredMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped})`;
  } else if (typesKilledMapped.length > 0) {
    whereClause += `AND (${typesKilledMapped})`;
  } else if (noInjuryFatality) {
    whereClause += sls`AND
      number_of_cyclist_injured = 0 AND
      number_of_cyclist_killed = 0 AND
      number_of_motorist_injured = 0 AND
      number_of_motorist_killed = 0 AND
      number_of_pedestrian_injured = 0 AND
      number_of_pedestrian_killed = 0 AND
      number_of_persons_injured = 0 AND
      number_of_persons_killed = 0
    `;
  }

  return whereClause;
};

// Links the Filter by Boundary button name to corresponding Carto table name
export const filterAreaBtnTableMap = {
  Borough: nyc_borough,
  'Community Board': nyc_community_board,
  'City Council District': nyc_city_council,
  'Neighborhood (NTA)': nyc_neighborhood,
  'NYPD Precinct': nyc_nypd_precinct,
  'Zipcode (ZCTA)': nyc_zip_code,
};

// Creates the spatial join clause with a boundary table geom
// @param {string} areaName, name of boundary, eg 'Borough' or 'City Council Districts'
const joinToGeoTableClause = (areaName) => {
  const geoTable = filterAreaBtnTableMap[areaName];
  if (geoTable) {
    return sls`
      JOIN ${geoTable} a
      ON ST_Within(c.the_geom, a.the_geom)
    `;
  }
  return '';
};

// Creates the WHERE clause for boundary table identifier
// @param {number || string} identifier, unique id of boundary polygon
const filterByIdentifierWhereClause = (identifier) => {
  if (identifier) {
    return `AND a.identifier = $$${identifier}$$`;
  }
  return '';
};

// Creates the PostGIS query for selecting crash data by custom area created by Leaflet.Draw
// @param {array} lonLatArray, an array of longitude, latitude arrays that form an enclosed polygon
const filterByCustomAreaClause = (lonLatArray) => {
  if (lonLatArray && lonLatArray.length) {
    // PostGIS GeomFromText expects lon lat coords like (-73.91 40.74, -73.89 40.73, ...)
    const coordinates = lonLatArray.map(lonLat => lonLat.join(' '));
    return sls`
      AND
        ST_Contains(
          ST_GeomFromText(
            'POLYGON(( ${coordinates} ))',
          4326),
          c.the_geom
        )
    `;
  }
  return '';
};

/*
 ********************************** MAP ****************************************
 */

// Generates the SQL query for the Carto layer based on filter params & app element
// @param {object} params: key values associated with filters derived from app state
// @param {string} startDate: min date; required
// @param {string} endDate: max date; required
// @param {string} harm: crash type, one of 'ALL', 'cyclist', 'motorist', 'ped'
// @param {string} persona: crash type, of of 'ALL', 'fatality', 'injury', 'no inj/fat'
export const configureMapSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT
      c.the_geom,
      c.the_geom_webmercator,
      c.on_street_name,
      c.cross_street_name,
      COUNT(c.cartodb_id) as total_crashes,
      SUM(c.number_of_cyclist_injured) as cyclist_injured,
      SUM(c.number_of_cyclist_killed) as cyclist_killed,
      SUM(c.number_of_motorist_injured) as motorist_injured,
      SUM(c.number_of_motorist_killed) as motorist_killed,
      SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
      SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
      SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
      SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
    FROM
      ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
      (date_val <= date '${endDate}')
    AND
      (date_val >= date '${startDate}')
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier)}
    AND
      c.the_geom IS NOT NULL
    GROUP BY
      c.the_geom, c.the_geom_webmercator, c.on_street_name, c.cross_street_name
  `;
};

/*
 ******************************* STATS *****************************************
 */

export const configureStatsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT
      COUNT(c.cartodb_id) as total_crashes,
      SUM(c.number_of_cyclist_injured) as cyclist_injured,
      SUM(c.number_of_cyclist_killed) as cyclist_killed,
      SUM(c.number_of_motorist_injured) as motorist_injured,
      SUM(c.number_of_motorist_killed) as motorist_killed,
      SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
      SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
      SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
      SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
    FROM
      ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
      (date_val <= date '${endDate}')
    AND
      (date_val >= date '${startDate}')
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier)}
  `;
};

/*
 *************************** CONTRIBUTING FACTORS ******************************
*/

export const configureFactorsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    WITH all_factors as (
      SELECT
        c.contributing_factor_vehicle_1 as factor
      FROM
        ${nyc_crashes} c
      ${joinToGeoTableClause(geo)} ${filterByIdentifierWhereClause(identifier)}
      WHERE
        (date_val <= date '${endDate}')
      AND
        (date_val >= date '${startDate}')
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
    )
    SELECT
     COUNT(af.factor) as count_factor,
     af.factor
    FROM
      all_factors af
    GROUP BY
      af.factor
    ORDER BY
      count_factor desc
  `;
};

/*
 ******************************* Download Data *********************************
*/

// Creates the SQL query for "Download Data" buttons
export const configureDownloadDataSQL = (params) => {
  const { startDate, endDate, filterArea, filterType } = params;
  const { geo, lngLats, identifier } = filterArea;

  return sls`
    SELECT
      c.cartodb_id,
      c.unique_key,
      c.the_geom,
      c.on_street_name,
      c.cross_street_name,
      c.date_val AS date,
      c.latitude,
      c.longitude,
      c.borough,
      c.zip_code,
      c.number_of_cyclist_injured,
      c.number_of_cyclist_killed,
      c.number_of_motorist_injured,
      c.number_of_motorist_killed,
      c.number_of_pedestrian_injured,
      c.number_of_pedestrian_killed,
      c.number_of_persons_injured,
      c.number_of_persons_killed,
      c.contributing_factor_vehicle_1,
      c.contributing_factor_vehicle_2,
      c.contributing_factor_vehicle_3,
      c.contributing_factor_vehicle_4,
      c.contributing_factor_vehicle_5,
      c.vehicle_type_code_1,
      c.vehicle_type_code_2,
      c.vehicle_type_code_3,
      c.vehicle_type_code_4,
      c.vehicle_type_code_5
    FROM ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
      (date_val <= date '${endDate}')
    AND
      (date_val >= date '${startDate}')
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier)}
  `;
};
<commit_msg>Wrap map sql query in a subquery for order by clause, closes #10<commit_after>import sls from 'single-line-string';

import { cartoTables } from './app_config';

const { nyc_borough,
  nyc_city_council,
  nyc_community_board,
  nyc_neighborhood,
  nyc_nypd_precinct,
  nyc_zip_code,
  nyc_crashes } = cartoTables;

// Links each boundary filter name to a SQL query
export const filterByAreaSQL = {
  Borough: sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_borough}
    ORDER BY
      identifier
  `,

  'City Council District': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_city_council}
    ORDER BY
      identifier
  `,

  'Community Board': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_community_board}
    ORDER BY
      identifier
  `,

  'Neighborhood (NTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_neighborhood}
    ORDER BY
      borough, identifier
  `,

  'NYPD Precinct': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_nypd_precinct}
    ORDER BY
      borough, identifier
  `,

  'Zipcode (ZCTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_zip_code}
    ORDER BY
      borough, identifier
  `,
};

// Selects max and min date from all crashes
export const dateBounds = () => sls`
  SELECT
    MAX(date) as max_date,
    MIN(date) as min_date
  FROM
    ${nyc_crashes}
`;

/*
***************************** SQL HELPERS **************************************
*/

// Generates the SQL WHERE clause for "Filter by Type"
// @param {object} the store.filterType piece of state
const filterByTypeWhereClause = (filterType) => {
  const { injury, fatality, noInjuryFatality } = filterType;
  let whereClause = '';

  const mapTypes = (personTypes, harmType) =>
    Object.keys(personTypes).filter((type) => {
      const val = personTypes[type];
      if (val) return type;
      return false;
    })
    .map((type) => {
      const hurtTerm = harmType === 'injury' ? 'injured' : 'killed';
      return ` number_of_${type}_${hurtTerm} > 0 `;
    })
    .join('OR');

  const typesInjuredMapped = mapTypes(injury, 'injury');
  const typesKilledMapped = mapTypes(fatality, 'fatality');

  if (typesInjuredMapped.length > 0 && typesKilledMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped} OR ${typesKilledMapped})`;
  } else if (typesInjuredMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped})`;
  } else if (typesKilledMapped.length > 0) {
    whereClause += `AND (${typesKilledMapped})`;
  } else if (noInjuryFatality) {
    whereClause += sls`AND
      number_of_cyclist_injured = 0 AND
      number_of_cyclist_killed = 0 AND
      number_of_motorist_injured = 0 AND
      number_of_motorist_killed = 0 AND
      number_of_pedestrian_injured = 0 AND
      number_of_pedestrian_killed = 0 AND
      number_of_persons_injured = 0 AND
      number_of_persons_killed = 0
    `;
  }

  return whereClause;
};

// Links the Filter by Boundary button name to corresponding Carto table name
export const filterAreaBtnTableMap = {
  Borough: nyc_borough,
  'Community Board': nyc_community_board,
  'City Council District': nyc_city_council,
  'Neighborhood (NTA)': nyc_neighborhood,
  'NYPD Precinct': nyc_nypd_precinct,
  'Zipcode (ZCTA)': nyc_zip_code,
};

// Creates the spatial join clause with a boundary table geom
// @param {string} areaName, name of boundary, eg 'Borough' or 'City Council Districts'
const joinToGeoTableClause = (areaName) => {
  const geoTable = filterAreaBtnTableMap[areaName];
  if (geoTable) {
    return sls`
      JOIN ${geoTable} a
      ON ST_Within(c.the_geom, a.the_geom)
    `;
  }
  return '';
};

// Creates the WHERE clause for boundary table identifier
// @param {number || string} identifier, unique id of boundary polygon
const filterByIdentifierWhereClause = (identifier) => {
  if (identifier) {
    return `AND a.identifier = $$${identifier}$$`;
  }
  return '';
};

// Creates the PostGIS query for selecting crash data by custom area created by Leaflet.Draw
// @param {array} lonLatArray, an array of longitude, latitude arrays that form an enclosed polygon
const filterByCustomAreaClause = (lonLatArray) => {
  if (lonLatArray && lonLatArray.length) {
    // PostGIS GeomFromText expects lon lat coords like (-73.91 40.74, -73.89 40.73, ...)
    const coordinates = lonLatArray.map(lonLat => lonLat.join(' '));
    return sls`
      AND
        ST_Contains(
          ST_GeomFromText(
            'POLYGON(( ${coordinates} ))',
          4326),
          c.the_geom
        )
    `;
  }
  return '';
};

/*
 ********************************** MAP ****************************************
 */

// Generates the SQL query for the Carto layer based on filter params & app element
// @param {object} params: key values associated with filters derived from app state
// @param {string} startDate: min date; required
// @param {string} endDate: max date; required
// @param {string} harm: crash type, one of 'ALL', 'cyclist', 'motorist', 'ped'
// @param {string} persona: crash type, of of 'ALL', 'fatality', 'injury', 'no inj/fat'
export const configureMapSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT * FROM
    (
      SELECT
        c.the_geom as the_geom,
        c.the_geom_webmercator as the_geom_webmercator,
        c.on_street_name as on_street_name,
        c.cross_street_name as cross_street_name,
        COUNT(c.cartodb_id) as total_crashes,
        SUM(c.number_of_cyclist_injured) as cyclist_injured,
        SUM(c.number_of_cyclist_killed) as cyclist_killed,
        SUM(c.number_of_motorist_injured) as motorist_injured,
        SUM(c.number_of_motorist_killed) as motorist_killed,
        SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
        SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
        SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
        SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
      FROM
        ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
        (date_val <= date '${endDate}')
      AND
        (date_val >= date '${startDate}')
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier)}
      AND
        c.the_geom IS NOT NULL
      GROUP BY
        c.the_geom, c.the_geom_webmercator, c.on_street_name, c.cross_street_name
    ) _
    ORDER BY
    CASE WHEN (persons_killed > 0) THEN 3
    WHEN (persons_injured > 0) THEN 2
    ELSE 1
    END
  `;
};

/*
 ******************************* STATS *****************************************
 */

export const configureStatsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT
      COUNT(c.cartodb_id) as total_crashes,
      SUM(c.number_of_cyclist_injured) as cyclist_injured,
      SUM(c.number_of_cyclist_killed) as cyclist_killed,
      SUM(c.number_of_motorist_injured) as motorist_injured,
      SUM(c.number_of_motorist_killed) as motorist_killed,
      SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
      SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
      SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
      SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
    FROM
      ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
      (date_val <= date '${endDate}')
    AND
      (date_val >= date '${startDate}')
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier)}
  `;
};

/*
 *************************** CONTRIBUTING FACTORS ******************************
*/

export const configureFactorsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    WITH all_factors as (
      SELECT
        c.contributing_factor_vehicle_1 as factor
      FROM
        ${nyc_crashes} c
      ${joinToGeoTableClause(geo)} ${filterByIdentifierWhereClause(identifier)}
      WHERE
        (date_val <= date '${endDate}')
      AND
        (date_val >= date '${startDate}')
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
    )
    SELECT
     COUNT(af.factor) as count_factor,
     af.factor
    FROM
      all_factors af
    GROUP BY
      af.factor
    ORDER BY
      count_factor desc
  `;
};

/*
 ******************************* Download Data *********************************
*/

// Creates the SQL query for "Download Data" buttons
export const configureDownloadDataSQL = (params) => {
  const { startDate, endDate, filterArea, filterType } = params;
  const { geo, lngLats, identifier } = filterArea;

  return sls`
    SELECT
      c.cartodb_id,
      c.unique_key,
      c.the_geom,
      c.on_street_name,
      c.cross_street_name,
      c.date_val AS date,
      c.latitude,
      c.longitude,
      c.borough,
      c.zip_code,
      c.number_of_cyclist_injured,
      c.number_of_cyclist_killed,
      c.number_of_motorist_injured,
      c.number_of_motorist_killed,
      c.number_of_pedestrian_injured,
      c.number_of_pedestrian_killed,
      c.number_of_persons_injured,
      c.number_of_persons_killed,
      c.contributing_factor_vehicle_1,
      c.contributing_factor_vehicle_2,
      c.contributing_factor_vehicle_3,
      c.contributing_factor_vehicle_4,
      c.contributing_factor_vehicle_5,
      c.vehicle_type_code_1,
      c.vehicle_type_code_2,
      c.vehicle_type_code_3,
      c.vehicle_type_code_4,
      c.vehicle_type_code_5
    FROM ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
      (date_val <= date '${endDate}')
    AND
      (date_val >= date '${startDate}')
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier)}
  `;
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';

export default class SearchBar extends Component {
  render() {
    return(
      <form className="input-group">
        <input />
      </form>
    );
  }
}
<commit_msg>Search button added to the SearchBar<commit_after>import React, { Component } from 'react';

export default class SearchBar extends Component {
  render() {
    return(
      <form className="input-group">
        <input />
        <span className="input-group-btn">
          <button type="submit" className="btn btn-secondary">Search</button>
        </span>
      </form>
    );
  }
}
<|endoftext|>


<commit_before>/**
 * OpenEyes
 *
 * (C) Moorfields Eye Hospital NHS Foundation Trust, 2008-2011
 * (C) OpenEyes Foundation, 2011-2013
 * This file is part of OpenEyes.
 * OpenEyes is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * OpenEyes is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with OpenEyes in a file titled COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package OpenEyes
 * @link http://www.openeyes.org.uk
 * @author OpenEyes <info@openeyes.org.uk>
 * @copyright Copyright (c) 2008-2011, Moorfields Eye Hospital NHS Foundation Trust
 * @copyright Copyright (c) 2011-2013, OpenEyes Foundation
 * @license http://www.gnu.org/licenses/gpl-3.0.html The GNU General Public License V3.0
 */

(function(exports) {
    /**
     * OpenEyes Dash namespace
     * @namespace OpenEyes.Dash
     * @memberOf OpenEyes
     */
    var Dash = {
        reports: {},
        itemWrapper: '

'
    };

    /**
     * appends a wrapper to the grid for a dash item
     *
     * @param report
     * @param size
     * @returns {string}
     */
    function appendDashWrapper(report, size) {
        var container,
            id;

        if(size == undefined){
            size = 6;
        }

        id = report.replace(new RegExp('[\W/:?=\\\\]', 'g'), '_');
        Dash.$container.append($(Dash.itemWrapper.replace('{$id}', id).replace('{$size}', size)));
        container = '#' + id;
        return container;
    }

    /**
     * Loads a piece of HTML in to a dash wrapper.
     *
     * @param report
     * @param wrapper
     */
    function loadBespokeHtml(report, wrapper) {
        $.ajax({
                url: report,
                dataType: 'html',
                success: function (data, textStatus, jqXHR) {
                    $(wrapper).html(data);
                    Dash.upgradeMaterial();
                    Dash.selectCheckList(wrapper);
                }
            }
        );
    }

    /**
     * Inits the Dash.
     *
     * @param container
     */
    Dash.init = function(container)
    {
        var $dateInputs = $('#from-date, #to-date');
        Dash.$container = $(container);

        Dash.$container.on('click', '.search-icon', function(){
            $(this).parent('.report-container').find('.report-search').removeClass('visuallyhidden').animate({
                height: '100%'
            }, 300);
        });

        $dateInputs.on('focus', function(){
            $(this).parent().addClass('is-dirty');
        });
        $dateInputs.datepicker({
            prevText: "chevron_left",
            nextText: "chevron_right",
            dateFormat: 'd M yy',
            onClose: function(date, inst) {
                if(!date){
                    $(inst.input).parent().removeClass('is-dirty');
                }
            }
        });

        $('#search-form').on('submit', function(e){
            e.preventDefault();

            $('.report-search-form').trigger('submit');

        });

        Dash.$container.on('submit', '.report-search-form', function(e){
            e.preventDefault();
            var chart,
                $searchForm = $(this),
                chartId = $searchForm.parents('.report-container').find('.chart-container').attr('id');

            $.ajax({
                url: $(this).attr('action'),
                data: $searchForm.serialize() + '&' + $('#search-form').serialize(),
                dataType: 'json',
                success: function (data, textStatus, jqXHR) {
                    chart = OpenEyes.Dash.reports[chartId];
                    chart.series[0].setData(data);

                    if(typeof Dash.postUpdate[chartId] === 'function'){
                        Dash.postUpdate[chartId](data);
                    }

                    $searchForm.parent('.report-search').animate({
                        height: '0'
                    }, 300, function(){
                        $(this).addClass('visuallyhidden');
                    });
                }
            });
        });
    };

    /**
     * Upgrade elements from ajax with material design javascript
     */
    Dash.upgradeMaterial = function()
    {
        var mdlUpgrades = {
            MaterialRadio: '.mdl-radio',
            MaterialCheckbox: '.mdl-checkbox'
        };
        for(var upgrade in mdlUpgrades){
            if(!mdlUpgrades.hasOwnProperty(upgrade)){
                continue;
            }
            if(typeof componentHandler !== "undefined"){
                var elements = document.querySelectorAll(mdlUpgrades[upgrade]);
                for(var i = 0; i < elements.length; i++){
                    componentHandler.upgradeElement(elements[i], upgrade);
                }
            }
        }
    };

    Dash.selectCheckList = function(wrapper)
    {

        $(wrapper).find('.checkbox-select').each(function(){
            var $checkboxes;

            $checkboxes = $(this).find(':input[type="checkbox"]');
            $checkboxes.on('change', function(){
                if(this.value == 'all' && this.checked){
                    $checkboxes.filter(':input[value!="all"]').removeAttr('checked').parents('label').removeClass('is-checked');
                } else {
                    $checkboxes.filter(':input[value="all"]').removeAttr('checked').parents('label').removeClass('is-checked');
                }
            })
        });
    };

    Dash.addBespokeReport = function(report, dependency, size)
    {
        var wrapper;

        wrapper = appendDashWrapper(report, size);
        Dash.loadBespokeReport(report, dependency, wrapper)
    };

    Dash.updateBespokeReport = function(report, wrapper)
    {
        loadBespokeHtml(report, wrapper);
    };

    Dash.loadBespokeReport = function(report, dependency, wrapper)
    {
        if(dependency){
            $.getScript(dependency, function(){
                loadBespokeHtml(report, wrapper);
            });
        } else {
            loadBespokeHtml(report, wrapper);
        }
    };

    Dash.postUpdate = {
        'PcrRiskReport': function(data){
            var chart = OpenEyes.Dash.reports['PcrRiskReport'];
            chart.setTitle(null, {text: 'Total Operations: ' +  data[0][0]} );
            if($('#pcr-risk-mode').val() == 1){
                chart.series[1].hide();
                chart.series[2].hide();
            }else if($('#pcr-risk-mode').val() == 0){
                chart.series[1].show();
                chart.series[2].show();
            }
        },
        'OEModule_OphCiExamination_components_RefractiveOutcomeReport': function(data){
            var total = 0,
                plusOrMinusOne = 0,
                plusOrMinusHalf = 0,
                plusOrMinusOnePercent = 0,
                plusOrMinusHalfPercent = 0,
                chart = OpenEyes.Dash.reports['OEModule_OphCiExamination_components_RefractiveOutcomeReport'];

            for(var i = 0; i < data.length; i++){
                total += parseInt(data[i][1], 10);

                // 18 and 22 are the indexes of the -1 and +1 columns
                if(data[i][0] < 18 || data[i][0] > 22){
                    plusOrMinusOne += parseFloat(data[i][1], 10);
                }

                // 19 and 21 are the indexes of the -0.5 and +0.5 columns
                if(data[i][0] < 19 || data[i][0] > 21){
                    plusOrMinusHalf += parseFloat(data[i][1], 10);
                }
            }

            plusOrMinusHalfPercent = plusOrMinusOne > 0 ? ( (plusOrMinusOne / total) * 100 ) : 0;
            plusOrMinusOnePercent = plusOrMinusHalf > 0 ? ( (plusOrMinusHalf / total) * 100 ) : 0;

            chart.setTitle(null, {text: 'Total eyes: ' + total + ', ±0.5D: ' + Number(plusOrMinusOnePercent).toFixed(1) + '%, ±1D: ' + Number(plusOrMinusHalfPercent).toFixed(1) + '%'});
        },
        'CataractComplicationsReport': function(data){
            $.ajax({
                data: $('#search-form').serialize(),
                url: "/OphTrOperationnote/report/cataractComplicationTotal",
                success: function (data, textStatus, jqXHR) {
                    var chart = OpenEyes.Dash.reports['CataractComplicationsReport'];
                    chart.setTitle(null, {text: 'Total Complications: ' + data[0] + " Total Operations: " + data[1]} );
                }
            });
        },
        'OEModule_OphCiExamination_components_VisualOutcomeReport':function(data){
            var months = $('#visual-acuity-months').val();
            var type = $('input[name="type"]:checked').val();
            var type_text = type.charAt(0).toUpperCase() + type.slice(1);

            var length = data.length;
            var total = 0;

            if( length ){
                for(var i = 0; i < length; i++){
                    total += data[i][2];
                }
            }

            OpenEyes.Dash.reports['OEModule_OphCiExamination_components_VisualOutcomeReport'].yAxis[0].setTitle({ text: "Visual acuity " + months + " month" + (months > 1 ? 's' : '') + " after surgery (LogMAR)" });
            OpenEyes.Dash.reports['OEModule_OphCiExamination_components_VisualOutcomeReport'].setTitle({ text: "Visual Acuity (" + type_text + ")" },{text: "Total Eyes: " + total});
        }
    };

    exports.Dash = Dash;
}(this.OpenEyes));<commit_msg>- average values added back<commit_after>/**
 * OpenEyes
 *
 * (C) Moorfields Eye Hospital NHS Foundation Trust, 2008-2011
 * (C) OpenEyes Foundation, 2011-2013
 * This file is part of OpenEyes.
 * OpenEyes is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * OpenEyes is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with OpenEyes in a file titled COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package OpenEyes
 * @link http://www.openeyes.org.uk
 * @author OpenEyes <info@openeyes.org.uk>
 * @copyright Copyright (c) 2008-2011, Moorfields Eye Hospital NHS Foundation Trust
 * @copyright Copyright (c) 2011-2013, OpenEyes Foundation
 * @license http://www.gnu.org/licenses/gpl-3.0.html The GNU General Public License V3.0
 */

(function(exports) {
    /**
     * OpenEyes Dash namespace
     * @namespace OpenEyes.Dash
     * @memberOf OpenEyes
     */
    var Dash = {
        reports: {},
        itemWrapper: '

'
    };

    /**
     * appends a wrapper to the grid for a dash item
     *
     * @param report
     * @param size
     * @returns {string}
     */
    function appendDashWrapper(report, size) {
        var container,
            id;

        if(size == undefined){
            size = 6;
        }

        id = report.replace(new RegExp('[\W/:?=\\\\]', 'g'), '_');
        Dash.$container.append($(Dash.itemWrapper.replace('{$id}', id).replace('{$size}', size)));
        container = '#' + id;
        return container;
    }

    /**
     * Loads a piece of HTML in to a dash wrapper.
     *
     * @param report
     * @param wrapper
     */
    function loadBespokeHtml(report, wrapper) {
        $.ajax({
                url: report,
                dataType: 'html',
                success: function (data, textStatus, jqXHR) {
                    $(wrapper).html(data);
                    Dash.upgradeMaterial();
                    Dash.selectCheckList(wrapper);
                }
            }
        );
    }

    /**
     * Inits the Dash.
     *
     * @param container
     */
    Dash.init = function(container)
    {
        var $dateInputs = $('#from-date, #to-date');
        Dash.$container = $(container);

        Dash.$container.on('click', '.search-icon', function(){
            $(this).parent('.report-container').find('.report-search').removeClass('visuallyhidden').animate({
                height: '100%'
            }, 300);
        });

        $dateInputs.on('focus', function(){
            $(this).parent().addClass('is-dirty');
        });
        $dateInputs.datepicker({
            prevText: "chevron_left",
            nextText: "chevron_right",
            dateFormat: 'd M yy',
            onClose: function(date, inst) {
                if(!date){
                    $(inst.input).parent().removeClass('is-dirty');
                }
            }
        });

        $('#search-form').on('submit', function(e){
            e.preventDefault();

            $('.report-search-form').trigger('submit');

        });

        Dash.$container.on('submit', '.report-search-form', function(e){
            e.preventDefault();
            var chart,
                $searchForm = $(this),
                chartId = $searchForm.parents('.report-container').find('.chart-container').attr('id');

            $.ajax({
                url: $(this).attr('action'),
                data: $searchForm.serialize() + '&' + $('#search-form').serialize(),
                dataType: 'json',
                success: function (data, textStatus, jqXHR) {
                    chart = OpenEyes.Dash.reports[chartId];
                    chart.series[0].setData(data);

                    if(typeof Dash.postUpdate[chartId] === 'function'){
                        Dash.postUpdate[chartId](data);
                    }

                    $searchForm.parent('.report-search').animate({
                        height: '0'
                    }, 300, function(){
                        $(this).addClass('visuallyhidden');
                    });
                }
            });
        });
    };

    /**
     * Upgrade elements from ajax with material design javascript
     */
    Dash.upgradeMaterial = function()
    {
        var mdlUpgrades = {
            MaterialRadio: '.mdl-radio',
            MaterialCheckbox: '.mdl-checkbox'
        };
        for(var upgrade in mdlUpgrades){
            if(!mdlUpgrades.hasOwnProperty(upgrade)){
                continue;
            }
            if(typeof componentHandler !== "undefined"){
                var elements = document.querySelectorAll(mdlUpgrades[upgrade]);
                for(var i = 0; i < elements.length; i++){
                    componentHandler.upgradeElement(elements[i], upgrade);
                }
            }
        }
    };

    Dash.selectCheckList = function(wrapper)
    {

        $(wrapper).find('.checkbox-select').each(function(){
            var $checkboxes;

            $checkboxes = $(this).find(':input[type="checkbox"]');
            $checkboxes.on('change', function(){
                if(this.value == 'all' && this.checked){
                    $checkboxes.filter(':input[value!="all"]').removeAttr('checked').parents('label').removeClass('is-checked');
                } else {
                    $checkboxes.filter(':input[value="all"]').removeAttr('checked').parents('label').removeClass('is-checked');
                }
            })
        });
    };

    Dash.addBespokeReport = function(report, dependency, size)
    {
        var wrapper;

        wrapper = appendDashWrapper(report, size);
        Dash.loadBespokeReport(report, dependency, wrapper)
    };

    Dash.updateBespokeReport = function(report, wrapper)
    {
        loadBespokeHtml(report, wrapper);
    };

    Dash.loadBespokeReport = function(report, dependency, wrapper)
    {
        if(dependency){
            $.getScript(dependency, function(){
                loadBespokeHtml(report, wrapper);
            });
        } else {
            loadBespokeHtml(report, wrapper);
        }
    };

    Dash.postUpdate = {
        'PcrRiskReport': function(data){
            var chart = OpenEyes.Dash.reports['PcrRiskReport'];
            chart.setTitle(null, {text: 'Total Operations: ' +  data[0][0]} );
        },
        'OEModule_OphCiExamination_components_RefractiveOutcomeReport': function(data){
            var total = 0,
                plusOrMinusOne = 0,
                plusOrMinusHalf = 0,
                plusOrMinusOnePercent = 0,
                plusOrMinusHalfPercent = 0,
                chart = OpenEyes.Dash.reports['OEModule_OphCiExamination_components_RefractiveOutcomeReport'];

            for(var i = 0; i < data.length; i++){
                total += parseInt(data[i][1], 10);

                // 18 and 22 are the indexes of the -1 and +1 columns
                if(data[i][0] < 18 || data[i][0] > 22){
                    plusOrMinusOne += parseFloat(data[i][1], 10);
                }

                // 19 and 21 are the indexes of the -0.5 and +0.5 columns
                if(data[i][0] < 19 || data[i][0] > 21){
                    plusOrMinusHalf += parseFloat(data[i][1], 10);
                }
            }

            plusOrMinusHalfPercent = plusOrMinusOne > 0 ? ( (plusOrMinusOne / total) * 100 ) : 0;
            plusOrMinusOnePercent = plusOrMinusHalf > 0 ? ( (plusOrMinusHalf / total) * 100 ) : 0;

            chart.setTitle(null, {text: 'Total eyes: ' + total + ', ±0.5D: ' + Number(plusOrMinusOnePercent).toFixed(1) + '%, ±1D: ' + Number(plusOrMinusHalfPercent).toFixed(1) + '%'});
        },
        'CataractComplicationsReport': function(data){
            $.ajax({
                data: $('#search-form').serialize(),
                url: "/OphTrOperationnote/report/cataractComplicationTotal",
                success: function (data, textStatus, jqXHR) {
                    var chart = OpenEyes.Dash.reports['CataractComplicationsReport'];
                    chart.setTitle(null, {text: 'Total Complications: ' + data[0] + " Total Operations: " + data[1]} );
                }
            });
        },
        'OEModule_OphCiExamination_components_VisualOutcomeReport':function(data){
            var months = $('#visual-acuity-months').val();
            var type = $('input[name="type"]:checked').val();
            var type_text = type.charAt(0).toUpperCase() + type.slice(1);

            var length = data.length;
            var total = 0;

            if( length ){
                for(var i = 0; i < length; i++){
                    total += data[i][2];
                }
            }

            OpenEyes.Dash.reports['OEModule_OphCiExamination_components_VisualOutcomeReport'].yAxis[0].setTitle({ text: "Visual acuity " + months + " month" + (months > 1 ? 's' : '') + " after surgery (LogMAR)" });
            OpenEyes.Dash.reports['OEModule_OphCiExamination_components_VisualOutcomeReport'].setTitle({ text: "Visual Acuity (" + type_text + ")" },{text: "Total Eyes: " + total});
        }
    };

    exports.Dash = Dash;
}(this.OpenEyes));<|endoftext|>


<commit_before>"use strict"
!function() {
  var DATE_EXT = {},
      ARR_EXT = {},
      OBJ_EXT = {};

  var extendMe;

  DATE_EXT.getLabel = (function() {
    return function() {
      if( isNaN( this.getTime() ) ) return '';

      return this.toString().split(' ').slice(1,4).join(' ');
    };
  })();

  DATE_EXT.getDelta = (function() {
    return function(deltaDate) {
      if( typeof deltaDate == "string" ) deltaDate = new Date(deltaDate);
      if( !this.isDate() || !deltaDate.isDate() ) return {};

      // Get # of milliseconds between dates
      var delta = this.getTime() > deltaDate.getTime() ?
                          this.getTime() - deltaDate.getTime() :
                          deltaDate.getTime() - this.getTime();

      // Set up some helper methods
      var toSecond = function(d) { return Math.floor( d / 1000 ); }
        , secondToMilli = function(d) { return d * 1000; }

        , toMinute = function(d) { return Math.floor( d / 1000 / 60 ); }
        , minuteToMilli = function(d) { return d * 1000 * 60; }

        , toHour = function(d) { return Math.floor( d / 1000 / 60 / 60 ); }
        , hourToMilli = function(d) { return d * 1000 * 60 * 60; }

        , toDay = function(d) { return Math.floor( d / 1000 / 60 / 60 / 24 ); }
        , dayToMilli = function(d) { return d * 1000 * 60 * 60 * 24; };

      // Calculate Days and remove from total delta
      var dayDelta = toDay( delta );
      delta -= dayToMilli( dayDelta );

      // Calculate hours, remove from total again
      var hourDelta = toHour( delta );
      delta -= hourToMilli( hourDelta );

      // Calculate minutes
      var minuteDelta = toMinute( delta );
      delta -= minuteToMilli( minuteDelta );

      // And seconds
      var secondDelta = toSecond( delta );
      delta -= secondToMilli( secondDelta );
      // Leftover delta === millisecond time delta

      // Return that shit!
      return {
        "days" : dayDelta,
        "hours" : hourDelta,
        "minutes" : minuteDelta,
        "seconds" : secondDelta,
        "milliseconds" : delta
      };
    };
  })();

  ARR_EXT.merge = (function() {
    return function(arr) {
      Array.prototype.push.apply(this, arr);
      return this;
    };
  })();

  OBJ_EXT.isDate = (function() {
    return function() {
      return  this instanceof Date &&
              this.getTime &&
              !isNaN( this.getTime() ) ||
              false;
    }
  })();

  OBJ_EXT.get = (function() {
    var _get = function(obj, path) {
      var key = path.shift(),
          lb, rb, idx, val;

      lb = key.lastIndexOf('[');
      rb = key.slice(lb).indexOf(']');

      if( lb !== -1 && rb !== -1 ) {
        rb += lb;
        var _idx = key.substring( lb + 1, rb );

        idx = parseInt(_idx, 10) || _idx;
        key = key.substring(0, lb);

        if(!key) {
          key = idx;
          idx = false;
        }
      }

      if (idx && obj[key]) {
        val = obj[key][idx];
      } else {
        val = obj[key];
      }

      if(!!val && path.length > 0) return _get(val, path);
      return val;
    };

    return function(loc) {
      var locArr = loc.split('.');
      var searchArr = [];

      for ( var i = 0, l = locArr.length; i < l; i++ ) {
        var piece = locArr[i].replace(/\[([a-z]+)\]/ig, '.$1');

        if ( i === 0 && piece[i] === '.') piece = piece.slice(1);

        searchArr.push.apply( searchArr, piece.split('.') );
      }

      return _get(this, searchArr);
    };
  })();

  String.prototype.formatPhone = function() {
    var numStr = this.replace(/[^0-9]/g, '');
    var result = '';

    while (numStr.length < 10) {
      numStr += '_';
    }

    result += '(' + numStr.substring(0, 3) + ') ';
    result += numStr.substring(3, 6) + '-';
    result += numStr.substring(6, 10);

    if (numStr.length > 10) {
      result += ' x' + numStr.substring(11);
    }

    return result;
  };

  String.prototype.trim = function(str) {
    var string = str || this,
        changed = false;

    if (string.substr(0, 1) == ' ') {
      string = string.substring(1);
      changed = true;
    }

    if (string.substr((string.length -1), 1) == ' ') {
      string = string.substr(0, (string.length - 1));
      changed = true;
    }

    return (changed ? this.trim(string) : string);
  };

  if (Object.defineProperty) {

    extendMe = function(nativeObj, extensions) {
      for (var extension in extensions) {
        Object.defineProperty( nativeObj.prototype, extension, {
          configurable  : true,
          writable      : true,
          enumerable    : false,
          value         : extensions[extension]
        });
      }
    };

  } else {

    extendMe = function(nativeObj, extensions) {
      for (var extension in extensions) {
        nativeObj.prototype[extension] = extensions[extension];
      }
    };
  }

  extendMe(Date, DATE_EXT);
  extendMe(Array, ARR_EXT);
  extendMe(Object, OBJ_EXT);
}();
<commit_msg>Fixed Bug<commit_after>"use strict"
!function() {
  var DATE_EXT = {},
      ARR_EXT = {},
      OBJ_EXT = {};

  var extendMe;

  DATE_EXT.getLabel = (function() {
    return function() {
      if( isNaN( this.getTime() ) ) return '';

      return this.toString().split(' ').slice(1,4).join(' ');
    };
  })();

  DATE_EXT.getDelta = (function() {
    return function(deltaDate) {
      if( typeof deltaDate == "string" ) deltaDate = new Date(deltaDate);
      if( !this.isDate() || !deltaDate.isDate() ) return {};

      // Get # of milliseconds between dates
      var delta = this.getTime() > deltaDate.getTime() ?
                          this.getTime() - deltaDate.getTime() :
                          deltaDate.getTime() - this.getTime();

      // Set up some helper methods
      var toSecond = function(d) { return Math.floor( d / 1000 ); }
        , secondToMilli = function(d) { return d * 1000; }

        , toMinute = function(d) { return Math.floor( d / 1000 / 60 ); }
        , minuteToMilli = function(d) { return d * 1000 * 60; }

        , toHour = function(d) { return Math.floor( d / 1000 / 60 / 60 ); }
        , hourToMilli = function(d) { return d * 1000 * 60 * 60; }

        , toDay = function(d) { return Math.floor( d / 1000 / 60 / 60 / 24 ); }
        , dayToMilli = function(d) { return d * 1000 * 60 * 60 * 24; };

      // Calculate Days and remove from total delta
      var dayDelta = toDay( delta );
      delta -= dayToMilli( dayDelta );

      // Calculate hours, remove from total again
      var hourDelta = toHour( delta );
      delta -= hourToMilli( hourDelta );

      // Calculate minutes
      var minuteDelta = toMinute( delta );
      delta -= minuteToMilli( minuteDelta );

      // And seconds
      var secondDelta = toSecond( delta );
      delta -= secondToMilli( secondDelta );
      // Leftover delta === millisecond time delta

      // Return that shit!
      return {
        "days" : dayDelta,
        "hours" : hourDelta,
        "minutes" : minuteDelta,
        "seconds" : secondDelta,
        "milliseconds" : delta
      };
    };
  })();

  ARR_EXT.merge = (function() {
    return function(arr) {
      Array.prototype.push.apply(this, arr);
      return this;
    };
  })();

  OBJ_EXT.isDate = (function() {
    return function() {
      return  this instanceof Date &&
              this.getTime &&
              !isNaN( this.getTime() ) ||
              false;
    }
  })();

  OBJ_EXT.get = (function() {
    var _get = function(obj, path) {
      var key = path.shift(),
          lb, rb, idx, val;

      lb = key.lastIndexOf('[');
      rb = key.slice(lb).indexOf(']');

      if( lb !== -1 && rb !== -1 ) {
        rb += lb;
        var _idx = key.substring( lb + 1, rb );

        idx = parseInt(_idx, 10) || _idx;
        key = key.substring(0, lb);

        if(!key) {
          key = idx;
          idx = false;
        }
      }

      if (idx && obj[key]) {
        val = obj[key][idx];
      } else {
        val = obj[key];
      }

      if(!!val && path.length > 0) return _get(val, path);
      return val;
    };

    return function(loc) {
      var locArr = loc.split('.');
      var searchArr = [];

      for ( var i = 0, l = locArr.length; i < l; i++ ) {
        var piece = locArr[i].replace(/\[([a-z]+)\]/ig, '.$1');

        if ( i === 0 && piece[i] === '.') piece = piece.slice(1);

        searchArr.push.apply( searchArr, piece.split('.') );
      }

      return _get(this, searchArr);
    };
  })();

  String.prototype.formatPhone = function() {
    var numStr = this.replace(/[^0-9]/g, '');
    var result = '';

    while (numStr.length < 10) {
      numStr += '_';
    }

    result += '(' + numStr.substring(0, 3) + ') ';
    result += numStr.substring(3, 6) + '-';
    result += numStr.substring(6, 10);

    if (numStr.length > 10) {
      result += ' x' + numStr.substring(10);
    }

    return result;
  };

  String.prototype.trim = function(str) {
    var string = str || this,
        changed = false;

    if (string.substr(0, 1) == ' ') {
      string = string.substring(1);
      changed = true;
    }

    if (string.substr((string.length -1), 1) == ' ') {
      string = string.substr(0, (string.length - 1));
      changed = true;
    }

    return (changed ? this.trim(string) : string);
  };

  if (Object.defineProperty) {

    extendMe = function(nativeObj, extensions) {
      for (var extension in extensions) {
        Object.defineProperty( nativeObj.prototype, extension, {
          configurable  : true,
          writable      : true,
          enumerable    : false,
          value         : extensions[extension]
        });
      }
    };

  } else {

    extendMe = function(nativeObj, extensions) {
      for (var extension in extensions) {
        nativeObj.prototype[extension] = extensions[extension];
      }
    };
  }

  extendMe(Date, DATE_EXT);
  extendMe(Array, ARR_EXT);
  extendMe(Object, OBJ_EXT);
}();
<|endoftext|>


<commit_before>/**
 * Modules are indiviual components that run regardless of the commands issued.
 * Require them here to be included in the bot
 */
require('./modules/reactionNav');
require('./modules/help');
require('./modules/hotline');
//require('./modules/logs');
require('./modules/api');
require('./modules/ingame');
require('./modules/accept');
<commit_msg>Disconnect accept module<commit_after>/**
 * Modules are indiviual components that run regardless of the commands issued.
 * Require them here to be included in the bot
 */
require('./modules/reactionNav');
require('./modules/help');
require('./modules/hotline');
//require('./modules/logs');
require('./modules/api');
require('./modules/ingame');
//require('./modules/accept');
<|endoftext|>


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

const LogUtil = require('../libs/LogUtil');

function httpsRequest(options, data, callback) {
  const req = https.request(options, (res) => {
    LogUtil.winston.log('info', `STATUS: ${res.statusCode}`);
    LogUtil.winston.log('info', `HEADERS: ${res.headers}`);

    // Set response encoding
    res.setEncoding('utf8');

    // On response send data
    res.on('data', (chunk) => {
      LogUtil.winston.log('info', `BODY: ${chunk}`);

      if (typeof callback === 'function') {
        callback(chunk);
      }
    });

    // On response end
    res.on('end', () => {
      LogUtil.winston.log('info', 'No more data in response.');
    });
  });

  // On response error
  req.on('error', (e) => {
    LogUtil.winston.log('error', `Problem with request: ${e.message}`);
  });

  // Write data to request body
  req.write(data);
  req.end();
}

module.exports = httpsRequest;
<commit_msg>fix(https request): fixed get request writing data<commit_after>const https = require('https');

const LogUtil = require('../libs/LogUtil');

function httpsRequest(options, data, callback) {
  const req = https.request(options, (res) => {
    LogUtil.winston.log('info', `STATUS: ${res.statusCode}`);
    LogUtil.winston.log('info', `HEADERS: ${res.headers}`);

    // Set response encoding
    res.setEncoding('utf8');

    // On response send data
    res.on('data', (chunk) => {
      LogUtil.winston.log('info', `BODY: ${chunk}`);

      if (typeof callback === 'function') {
        callback(chunk);
      }
    });

    // On response end
    res.on('end', () => {
      LogUtil.winston.log('info', 'No more data in response.');
    });
  });

  // On response error
  req.on('error', (e) => {
    LogUtil.winston.log('error', `Problem with request: ${e.message}`);
  });

  // Write data to request body
  if (options.method === 'POST' || options.method === 'PUT') {
    req.write(data);
  }

  req.end();
}

module.exports = httpsRequest;
<|endoftext|>


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

  var api = {
    get: function (action, params, api_ver=3) {
      var base_url = ckan.sandbox().client.endpoint;
      params = $.param(params);
      var url = base_url + '/api/' + api_ver + '/action/' + action + '?' + params;
      return $.getJSON(url);
    },
    post: function (action, data, api_ver=3) {
      var base_url = ckan.sandbox().client.endpoint;
      var url = base_url + '/api/' + api_ver + '/action/' + action;
      return $.post(url, JSON.stringify(data), "json");
    }
  };
  var mediaContainer = $('div[data-section="media"]');
  var caption = window.location.href;
  var twitterConsumerKey = window.twitterConsumerKey;
  var twitterConsumerSecret = window.twitterConsumerSecret;
  var twitterUserToken = localStorage.twitter_user_oauth_token;
  var twitterUserTokenSecret = localStorage.twitter_user_oauth_token_secret;
  var twitterOAuthToken = localStorage.twitter_oauth_token;
  var twitterOAuthTokenSecret = localStorage.twitter_oauth_token_secret;
  var cb, urlLocationSearch;

  if (twitterConsumerKey && twitterConsumerSecret) {
    cb = new Codebird;
    cb.setConsumerKey(twitterConsumerKey, twitterConsumerSecret);

    urlLocationSearch = location.search;

    // Request access token after the app is authorized
    if (urlLocationSearch.indexOf('oauth_token') > - 1 &&
        urlLocationSearch.indexOf('oauth_verifier') > - 1 &&
        !twitterUserToken &&
        !twitterUserTokenSecret) {
      _twitterGetAccessToken();
    }

    // If the authorization is denied then delete the oauth tokens
    if (urlLocationSearch !== '') {
      var hasDeniedQuery = urlLocationSearch.substr(1).indexOf('denied') > -1;

      if (hasDeniedQuery) {
        localStorage.removeItem('oauth_token');
        localStorage.removeItem('oauth_token_secret');
      }
    }

    if (twitterOAuthToken && twitterOAuthTokenSecret) {
      cb.setToken(twitterOAuthToken, twitterOAuthTokenSecret)
    }
  }

  mediaContainer.on('click', function onMediaContainerClick(event) {
    var target = $(event.target);
    var resourceViewId, message, className, duration, requestParams;

    if (target.hasClass('share-graph-fb-btn')) {
      FB.login(function(response) {
        if (response.authResponse) {
          var accessToken = FB.getAuthResponse()['accessToken'];
          var userID = FB.getAuthResponse()['userID'];

          var graphTitle = target.siblings('.graph-title').text();
          var svg;

          svg = target.parent('.graph-container').find('svg')[0];

          convertSVGGraphToImage(svg, graphTitle, function(imageData) {
            fbUpload(accessToken, userID, imageData, caption);
          });
        } else {
          console.log('User cancelled login or did not fully authorize.');
        }
      }, {scope: 'publish_actions'});
    } else if (target.hasClass('share-graph-twitter-btn')) {

      if (cb) { // Check of existence for a Codebird instance

        if (!localStorage.twitter_oauth_token) { // Request a token
          resourceViewId = target.siblings('.graph-container-resource-view').attr('data-id');

          message = 'Authenticating...';
          className = 'alert-info';
          duration = 1500;

          _showAlert(message, className, duration);

          requestParams = {
            oauth_callback: location.href + '?resource_view_id=' + resourceViewId
          };

          cb.__call('oauth_requestToken', requestParams, function(reply, rate) {
            if (reply.httpstatus === 200) {
              cb.setToken(reply.oauth_token, reply.oauth_token_secret);

              // Store the tokens in localStorage for later use
              localStorage.twitter_oauth_token = reply.oauth_token;
              localStorage.twitter_oauth_token_secret = reply.oauth_token_secret;

              // Authorize the application to access user's data
              cb.__call('oauth_authorize', {}, function(auth_url) {
                window.open(auth_url);
              });
            } else {
              message = 'An error occured.';
              className = 'alert-danger';
              duration = 3000;

              _showAlert(message, className, duration);
            }
          });
        } else if (!localStorage.twitter_user_oauth_token) {

          // Authorize the application to access user's data
          cb.__call('oauth_authorize', {}, function(auth_url) {
            window.open(auth_url);
          });
        } else {
          message = 'Sharing on Twitter...';
          className = 'alert-info';
          duration = 1500;

          _showAlert(message, className, duration);

          _shareGraphOnTwitter(target);
        }
      }
    }
  });

  function _shareGraphOnTwitter(target) {
    var graphTitle, svg;

    twitterUserToken = localStorage.twitter_user_oauth_token;
    twitterUserTokenSecret = localStorage.twitter_user_oauth_token_secret;

    if (twitterUserToken && twitterUserTokenSecret) {
      graphTitle = target.siblings('.graph-title').text();
      svg = target.parent('.graph-container').find('svg')[0];

      convertSVGGraphToImage(svg, graphTitle, function(imageData) {
        imageData = imageData.substr(22);
        imageData = encodeURIComponent(imageData);

        var params = {
          oauth_token: twitterUserToken,
          oauth_token_secret: twitterUserTokenSecret,
          image: imageData,
          graph_title: graphTitle,
          subdashboard_url: location.origin + location.pathname
        };
        var message, className, duration;

        api.post('orgportals_share_graph_on_twitter', params)
          .done(function(data) {
            if (data.success && data.result.share_status_success) {
              message = 'The graph is successfully shared on Twitter!';
              className = 'alert-success';
              duration = 3000;

              _showAlert(message, className, duration);
            } else {
              message = 'Error while sharing the graph on Twitter.';
              className = 'alert-danger';
              duration = 3000;

              _showAlert(message, className, duration);
            }
          })
          .error(function(error) {
            message = 'Error while sharing the graph on Twitter.';
            className = 'alert-danger';
            duration = 3000;

            _showAlert(message, className, duration);
          });
      });
    }
  }

  function _twitterGetAccessToken() {
    var current_url = location.toString();
    var query = current_url.match(/\?(.+)$/)[1].split('&');
    var parameters = {};
    var parameter;
    var requestParams;

    for (var i = 0; i < query.length; i++) {
      parameter = query[i].split('=');

      if (parameter.length === 1) {
          parameter[1] = '';
      }

      parameters[decodeURIComponent(parameter[0])] = decodeURIComponent(parameter[1]);
    }

    if (typeof parameters.oauth_verifier !== 'undefined') {
      cb.setToken(localStorage.twitter_oauth_token, localStorage.twitter_oauth_token_secret);

      requestParams = {
        oauth_verifier: parameters.oauth_verifier
      };

      cb.__call('oauth_accessToken', requestParams, function(reply, rate) {
        var resourceViewId, target, message, className, duration;

        if (reply.httpstatus === 200) {

          // Store the tokens in localStorage for later use
          localStorage.twitter_user_oauth_token = reply.oauth_token;
          localStorage.twitter_user_oauth_token_secret = reply.oauth_token_secret;

          resourceViewId = parameters.resource_view_id;

          target = $('div[data-id=' + resourceViewId + ']');

          message = 'Sharing on Twitter...';
          className = 'alert-info';
          duration = 1500;

          _showAlert(message, className, duration);

          _shareGraphOnTwitter(target);
        } else {
          message = 'An error occured.';
          className = 'alert-danger';
          duration = 3000;

          _showAlert(message, className, duration);
        }
      });
    }
  }

})($);

function _showAlert(message, className, duration) {
  var socialMediaShareAlert = $('.social-media-share-alert');

  socialMediaShareAlert.find('.alert-text').text(message);
  socialMediaShareAlert.addClass(className);
  socialMediaShareAlert.show();

  setTimeout(function() {
    socialMediaShareAlert.hide();
    socialMediaShareAlert.removeClass(className);
  }, duration);
}

function convertSVGGraphToImage(svg, graphTitle, callback) {
  var width = 0;
  var fontSize = 15;
  var lines = [];
  var i, j, result, canvasWidth, canvasHeight, img;
  var canvas = document.createElement('canvas');
  var ctx = canvas.getContext('2d');
  var svgData = new XMLSerializer().serializeToString(svg);
  var canvasWidth = Number(svg.getAttribute('width')) + 50;
  var canvasHeight = Number(svg.getAttribute('height')) + 100;

  canvas.style.backgroundColor = 'white';
  canvas.width = canvasWidth;
  canvas.height = canvasHeight;

  ctx.fillStyle = '#fff';
  ctx.fillRect(0, 0, canvasWidth, canvasHeight);

  ctx.fillStyle = '#000';
  ctx.font = fontSize + 'px Arial';

  // Split the graph's title into multiple lines if it's wider than the canvas's width
  while (graphTitle.length) {
    for (i = graphTitle.length; ctx.measureText(graphTitle.substr(0, i)).width > (canvasWidth - 20); i--);

    result = graphTitle.substr(0,i);

    if (i !== graphTitle.length) {
      for (j = 0; result.indexOf(' ', j) !== -1; j = result.indexOf(' ', j) + 1);
    }

    lines.push(result.substr(0, j || result.length));
    width = Math.max(width, ctx.measureText(lines[lines.length - 1]).width);
    graphTitle = graphTitle.substr(lines[lines.length - 1].length, graphTitle.length);
  }

  for (i = 0, j = lines.length; i < j; ++i) {
    ctx.fillText(lines[i], 20, 10 + fontSize + (fontSize + 5) * i);
  }

  img = document.createElement('img');
  img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(svgData));

  img.onload = function() {
    ctx.drawImage( img, 0, 70 );

    callback(canvas.toDataURL('image/png', 1.0));
  };
};

function fbUpload(accessToken, userID, imageData, caption) {
  var dataURL = imageData;
  var blob = dataURItoBlob(dataURL);
  var formData = new FormData();

  formData.append('token', accessToken);
  formData.append('source', blob);
  formData.append('caption', caption);

  var xhr = new XMLHttpRequest();

  xhr.open('POST', 'https://graph.facebook.com/'+ userID +'/photos?access_token=' + accessToken, true);

  xhr.onreadystatechange = function(data) {
    if (xhr.readyState === 4) {
      message = 'The graph is successfully shared on Facebook!';
      className = 'alert-success';
      duration = 3000;

      _showAlert(message, className, duration);
    }
  };

  xhr.onerror = function(error) {
    message = 'An error occured.';
    className = 'alert-danger';
    duration = 3000;

    _showAlert(message, className, duration);
  };

  xhr.send(formData);
}

function dataURItoBlob(dataURI) {
  var byteString = atob(dataURI.split(',')[1]);
  var ab = new ArrayBuffer(byteString.length);
  var ia = new Uint8Array(ab);
  for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); }
  return new Blob([ab], { type: 'image/png' });
}
<commit_msg>Show error message for facebook media share<commit_after>(function () {
  'use strict';

  var api = {
    get: function (action, params, api_ver=3) {
      var base_url = ckan.sandbox().client.endpoint;
      params = $.param(params);
      var url = base_url + '/api/' + api_ver + '/action/' + action + '?' + params;
      return $.getJSON(url);
    },
    post: function (action, data, api_ver=3) {
      var base_url = ckan.sandbox().client.endpoint;
      var url = base_url + '/api/' + api_ver + '/action/' + action;
      return $.post(url, JSON.stringify(data), "json");
    }
  };
  var mediaContainer = $('div[data-section="media"]');
  var caption = window.location.href;
  var twitterConsumerKey = window.twitterConsumerKey;
  var twitterConsumerSecret = window.twitterConsumerSecret;
  var twitterUserToken = localStorage.twitter_user_oauth_token;
  var twitterUserTokenSecret = localStorage.twitter_user_oauth_token_secret;
  var twitterOAuthToken = localStorage.twitter_oauth_token;
  var twitterOAuthTokenSecret = localStorage.twitter_oauth_token_secret;
  var cb, urlLocationSearch;

  if (twitterConsumerKey && twitterConsumerSecret) {
    cb = new Codebird;
    cb.setConsumerKey(twitterConsumerKey, twitterConsumerSecret);

    urlLocationSearch = location.search;

    // Request access token after the app is authorized
    if (urlLocationSearch.indexOf('oauth_token') > - 1 &&
        urlLocationSearch.indexOf('oauth_verifier') > - 1 &&
        !twitterUserToken &&
        !twitterUserTokenSecret) {
      _twitterGetAccessToken();
    }

    // If the authorization is denied then delete the oauth tokens
    if (urlLocationSearch !== '') {
      var hasDeniedQuery = urlLocationSearch.substr(1).indexOf('denied') > -1;

      if (hasDeniedQuery) {
        localStorage.removeItem('oauth_token');
        localStorage.removeItem('oauth_token_secret');
      }
    }

    if (twitterOAuthToken && twitterOAuthTokenSecret) {
      cb.setToken(twitterOAuthToken, twitterOAuthTokenSecret)
    }
  }

  mediaContainer.on('click', function onMediaContainerClick(event) {
    var target = $(event.target);
    var resourceViewId, message, className, duration, requestParams;

    if (target.hasClass('share-graph-fb-btn')) {
      FB.login(function(response) {
        if (response.authResponse) {
          var accessToken = FB.getAuthResponse()['accessToken'];
          var userID = FB.getAuthResponse()['userID'];

          var graphTitle = target.siblings('.graph-title').text();
          var svg;

          svg = target.parent('.graph-container').find('svg')[0];

          convertSVGGraphToImage(svg, graphTitle, function(imageData) {
            fbUpload(accessToken, userID, imageData, caption);
          });
        } else {
          console.log('User cancelled login or did not fully authorize.');
        }
      }, {scope: 'publish_actions'});
    } else if (target.hasClass('share-graph-twitter-btn')) {

      if (cb) { // Check of existence for a Codebird instance

        if (!localStorage.twitter_oauth_token) { // Request a token
          resourceViewId = target.siblings('.graph-container-resource-view').attr('data-id');

          message = 'Authenticating...';
          className = 'alert-info';
          duration = 1500;

          _showAlert(message, className, duration);

          requestParams = {
            oauth_callback: location.href + '?resource_view_id=' + resourceViewId
          };

          cb.__call('oauth_requestToken', requestParams, function(reply, rate) {
            if (reply.httpstatus === 200) {
              cb.setToken(reply.oauth_token, reply.oauth_token_secret);

              // Store the tokens in localStorage for later use
              localStorage.twitter_oauth_token = reply.oauth_token;
              localStorage.twitter_oauth_token_secret = reply.oauth_token_secret;

              // Authorize the application to access user's data
              cb.__call('oauth_authorize', {}, function(auth_url) {
                window.open(auth_url);
              });
            } else {
              message = 'An error occured.';
              className = 'alert-danger';
              duration = 3000;

              _showAlert(message, className, duration);
            }
          });
        } else if (!localStorage.twitter_user_oauth_token) {

          // Authorize the application to access user's data
          cb.__call('oauth_authorize', {}, function(auth_url) {
            window.open(auth_url);
          });
        } else {
          message = 'Sharing on Twitter...';
          className = 'alert-info';
          duration = 1500;

          _showAlert(message, className, duration);

          _shareGraphOnTwitter(target);
        }
      }
    }
  });

  function _shareGraphOnTwitter(target) {
    var graphTitle, svg;

    twitterUserToken = localStorage.twitter_user_oauth_token;
    twitterUserTokenSecret = localStorage.twitter_user_oauth_token_secret;

    if (twitterUserToken && twitterUserTokenSecret) {
      graphTitle = target.siblings('.graph-title').text();
      svg = target.parent('.graph-container').find('svg')[0];

      convertSVGGraphToImage(svg, graphTitle, function(imageData) {
        imageData = imageData.substr(22);
        imageData = encodeURIComponent(imageData);

        var params = {
          oauth_token: twitterUserToken,
          oauth_token_secret: twitterUserTokenSecret,
          image: imageData,
          graph_title: graphTitle,
          subdashboard_url: location.origin + location.pathname
        };
        var message, className, duration;

        api.post('orgportals_share_graph_on_twitter', params)
          .done(function(data) {
            if (data.success && data.result.share_status_success) {
              message = 'The graph is successfully shared on Twitter!';
              className = 'alert-success';
              duration = 3000;

              _showAlert(message, className, duration);
            } else {
              message = 'Error while sharing the graph on Twitter.';
              className = 'alert-danger';
              duration = 3000;

              _showAlert(message, className, duration);
            }
          })
          .error(function(error) {
            message = 'Error while sharing the graph on Twitter.';
            className = 'alert-danger';
            duration = 3000;

            _showAlert(message, className, duration);
          });
      });
    }
  }

  function _twitterGetAccessToken() {
    var current_url = location.toString();
    var query = current_url.match(/\?(.+)$/)[1].split('&');
    var parameters = {};
    var parameter;
    var requestParams;

    for (var i = 0; i < query.length; i++) {
      parameter = query[i].split('=');

      if (parameter.length === 1) {
          parameter[1] = '';
      }

      parameters[decodeURIComponent(parameter[0])] = decodeURIComponent(parameter[1]);
    }

    if (typeof parameters.oauth_verifier !== 'undefined') {
      cb.setToken(localStorage.twitter_oauth_token, localStorage.twitter_oauth_token_secret);

      requestParams = {
        oauth_verifier: parameters.oauth_verifier
      };

      cb.__call('oauth_accessToken', requestParams, function(reply, rate) {
        var resourceViewId, target, message, className, duration;

        if (reply.httpstatus === 200) {

          // Store the tokens in localStorage for later use
          localStorage.twitter_user_oauth_token = reply.oauth_token;
          localStorage.twitter_user_oauth_token_secret = reply.oauth_token_secret;

          resourceViewId = parameters.resource_view_id;

          target = $('div[data-id=' + resourceViewId + ']');

          message = 'Sharing on Twitter...';
          className = 'alert-info';
          duration = 1500;

          _showAlert(message, className, duration);

          _shareGraphOnTwitter(target);
        } else {
          message = 'An error occured.';
          className = 'alert-danger';
          duration = 3000;

          _showAlert(message, className, duration);
        }
      });
    }
  }

})($);

function _showAlert(message, className, duration) {
  var socialMediaShareAlert = $('.social-media-share-alert');

  socialMediaShareAlert.find('.alert-text').text(message);
  socialMediaShareAlert.addClass(className);
  socialMediaShareAlert.show();

  setTimeout(function() {
    socialMediaShareAlert.hide();
    socialMediaShareAlert.removeClass(className);
  }, duration);
}

function convertSVGGraphToImage(svg, graphTitle, callback) {
  var width = 0;
  var fontSize = 15;
  var lines = [];
  var i, j, result, canvasWidth, canvasHeight, img;
  var canvas = document.createElement('canvas');
  var ctx = canvas.getContext('2d');
  var svgData = new XMLSerializer().serializeToString(svg);
  var canvasWidth = Number(svg.getAttribute('width')) + 50;
  var canvasHeight = Number(svg.getAttribute('height')) + 100;

  canvas.style.backgroundColor = 'white';
  canvas.width = canvasWidth;
  canvas.height = canvasHeight;

  ctx.fillStyle = '#fff';
  ctx.fillRect(0, 0, canvasWidth, canvasHeight);

  ctx.fillStyle = '#000';
  ctx.font = fontSize + 'px Arial';

  // Split the graph's title into multiple lines if it's wider than the canvas's width
  while (graphTitle.length) {
    for (i = graphTitle.length; ctx.measureText(graphTitle.substr(0, i)).width > (canvasWidth - 20); i--);

    result = graphTitle.substr(0,i);

    if (i !== graphTitle.length) {
      for (j = 0; result.indexOf(' ', j) !== -1; j = result.indexOf(' ', j) + 1);
    }

    lines.push(result.substr(0, j || result.length));
    width = Math.max(width, ctx.measureText(lines[lines.length - 1]).width);
    graphTitle = graphTitle.substr(lines[lines.length - 1].length, graphTitle.length);
  }

  for (i = 0, j = lines.length; i < j; ++i) {
    ctx.fillText(lines[i], 20, 10 + fontSize + (fontSize + 5) * i);
  }

  img = document.createElement('img');
  img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(svgData));

  img.onload = function() {
    ctx.drawImage( img, 0, 70 );

    callback(canvas.toDataURL('image/png', 1.0));
  };
};

function fbUpload(accessToken, userID, imageData, caption) {
  var dataURL = imageData;
  var blob = dataURItoBlob(dataURL);
  var formData = new FormData();

  formData.append('token', accessToken);
  formData.append('source', blob);
  formData.append('caption', caption);

  var xhr = new XMLHttpRequest();

  xhr.open('POST', 'https://graph.facebook.com/'+ userID +'/photos?access_token=' + accessToken, true);

  xhr.onreadystatechange = function(data) {
    if (xhr.readyState==4 && xhr.status==200) {
      message = 'The graph is successfully shared on Facebook!';
      className = 'alert-success';
      duration = 3000;

      _showAlert(message, className, duration);
    } else {

      message = 'An error occured.';
      className = 'alert-danger';
      duration = 3000;

      _showAlert(message, className, duration);
    }
  };

  xhr.onerror = function(error) {
    message = 'An error occured.';
    className = 'alert-danger';
    duration = 3000;

    _showAlert(message, className, duration);
  };

  xhr.send(formData);
}

function dataURItoBlob(dataURI) {
  var byteString = atob(dataURI.split(',')[1]);
  var ab = new ArrayBuffer(byteString.length);
  var ia = new Uint8Array(ab);
  for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); }
  return new Blob([ab], { type: 'image/png' });
}
<|endoftext|>