<commit_before>var assert = require('assert'),
    vcharts = require('../src/index.js');

describe('transform', function () {
    describe('base', function () {
        it('should pass through basic structures', function () {
            assert.deepEqual({}, vcharts.transform({}));
            assert.deepEqual([], vcharts.transform([]));
            assert.deepEqual('hi', vcharts.transform('hi'));
            assert.deepEqual(1.2, vcharts.transform(1.2));
            assert.deepEqual(true, vcharts.transform(true));
            assert.deepEqual(null, vcharts.transform(null));
        });

        it('should pass through deeper objects', function () {
            var deeper = {
                a: [1, 2, 'abc'],
                b: {c: false},
                c: null
            };
            assert.deepEqual(deeper, vcharts.transform(deeper));
        });
    });

    describe('@get', function () {
        it('should lookup values', function () {
            var spec = ['@get', 'a'];
            assert.deepEqual(12, vcharts.transform(spec, {a: 12}));
        });

        it('should default values', function () {
            var spec = ['@get', 'a', 5];
            assert.deepEqual(5, vcharts.transform(spec, {b: 12}));
        });

        it('should only override undefined values', function () {
            var spec = ['@get', 'a', 5];
            assert.deepEqual(0, vcharts.transform(spec, {a: 0}));
            assert.deepEqual(false, vcharts.transform(spec, {a: false}));
            assert.deepEqual(5, vcharts.transform(spec, {a: undefined}));
        });

        it('should evaluate default values', function () {
            var spec = ['@get', 'a', ['@eq', 5, 2]];
            assert.deepEqual(false, vcharts.transform(spec, {b: 12}));
        });

        it('should not reuse default values later', function () {
            var spec = [['@get', 'a', 5], ['@get', 'a']];
            assert.deepEqual([5, null], vcharts.transform(spec, {b: 12}));
        });

        it('should not modify options', function () {
            var spec = [['@get', 'a', 5], ['@get', 'a']];
            var options = {b: 12};
            vcharts.transform(spec, options);
            assert.deepEqual({b: 12}, options);
        });
    });

    describe('@defaults', function () {
        it('should allow nested defaulting on defined parent', function () {
            var spec = [
                '@defaults',
                [['a.b', 5]],
                [['@get', 'a.b'], ['@get', 'a.d']]
            ];
            assert.deepEqual([5, 1], vcharts.transform(spec, {a: {d: 1}}));
        });

        it('should allow nested defaulting on undefined parent', function () {
            var spec = [
                '@defaults',
                [['a.b', 5]],
                ['@get', 'a.b']
            ];
            assert.deepEqual(5, vcharts.transform(spec));
        });

        it('should not override options', function () {
            var spec = [
                '@defaults',
                [['a', 5]],
                ['@get', 'a']
            ];
            assert.deepEqual(7, vcharts.transform(spec, {a: 7}));
        });

        it('should only override undefined values', function () {
            var spec = ['@defaults', [['a', 5]], ['@get', 'a']];
            assert.deepEqual(0, vcharts.transform(spec, {a: 0}));
            assert.deepEqual(false, vcharts.transform(spec, {a: false}));
            assert.deepEqual(5, vcharts.transform(spec, {a: undefined}));
        });
    });

    describe('@map', function () {
        it('should build an array', function () {
            var spec = ['@map', [1, 2, 3], 'd', ['@get', 'd']];
            assert.deepEqual([1, 2, 3], vcharts.transform(spec));
        });

        it('should not add null array items', function () {
            var spec = ['@map', [1, null, 3], 'd', ['@get', 'd']];
            assert.deepEqual([1, 3], vcharts.transform(spec));
        });

        it('can contain complex objects', function () {
            var spec = ['@map', [1, 2, 3], 'd', {a: ['@get', 'd']}];
            assert.deepEqual([{a: 1}, {a: 2}, {a: 3}], vcharts.transform(spec));
        });

        it('can nest', function () {
            var spec = [
                '@map',
                [1, 2, 3],
                'd',
                [
                    '@map',
                    ['a', 'b'],
                    'dd',
                    {
                        d: ['@get', 'd'],
                        dd: ['@get', 'dd']
                    }
                ]
            ];
            assert.deepEqual([
                [{d: 1, dd: 'a'}, {d: 1, dd: 'b'}],
                [{d: 2, dd: 'a'}, {d: 2, dd: 'b'}],
                [{d: 3, dd: 'a'}, {d: 3, dd: 'b'}]
            ], vcharts.transform(spec));
        });

        it('should not modify options', function () {
            var spec = ['@map', [1, 2, 3], 'd', ['@get', 'd']];
            var options = {b: 12};
            vcharts.transform(spec, options);
            assert.deepEqual({b: 12}, options);
        });

        it('should override option with loop variable', function () {
            var spec = ['@map', [1, 2, 3], 'd', ['@get', 'd']];
            var options = {d: 12};
            assert.deepEqual([1, 2, 3], vcharts.transform(spec, options));
            assert.deepEqual({d: 12}, options);
        });
    });

    describe('@if', function () {
        it('should choose first option when true', function () {
            var spec = ['@if', true, 10, 20];
            assert.deepEqual(10, vcharts.transform(spec));
        });

        it('should choose second option when false', function () {
            var spec = ['@if', false, 10, 20];
            assert.deepEqual(20, vcharts.transform(spec));
        });

        it('should work with sub-expressions', function () {
            var spec = ['@if', ['@get', 'a'], ['@get', 'b'], 20];
            assert.deepEqual(5, vcharts.transform(spec, {a: true, b: 5}));
        });

        it('should treat JavaScript falsy values as false', function () {
            var spec = [
                ['@if', null, 10, 20],
                ['@if', undefined, 10, 20],
                ['@if', 0, 10, 20],
                ['@if', NaN, 10, 20],
                ['@if', '', 10, 20]
            ]
            assert.deepEqual(
                [20, 20, 20, 20, 20],
                vcharts.transform(spec)
            );
        });
    });

    describe('@eq', function () {
        it('should test for JavaScript === equality', function () {
            var spec = [
                ['@eq', {}, {}],
                ['@eq', 0, 0],
                ['@eq', 'abc', 'abc'],
                ['@eq', 1, '1'],
                ['@eq', null, null]
            ]
            assert.deepEqual(
                [false, true, true, false, true],
                vcharts.transform(spec)
            );
        });

        it('can work with sub-expressions', function () {
            var spec = [
                ['@eq', ['@get', 'a'], 10],
                ['@eq', ['@get', 'b'], ['@get', 'c']]
            ];
            assert.deepEqual(
                [true, true],
                vcharts.transform(spec, {a: 10, b: 5, c: 5})
            );
        });
    });

    describe('@min', function () {
        it('should find minimum of array', function () {
            var spec = [
                '@min', [{v: 10}, {v: 2}, {v: 3}, {v: 1}, {v: 8}], 'v'
            ]
            assert.equal(1, vcharts.transform(spec));
        });
    });

    describe('@max', function () {
        it('should find maximum of array', function () {
            var spec = [
                '@max', [{v: 10}, {v: 2}, {v: 3}, {v: 1}, {v: 8}], 'v'
            ]
            assert.equal(10, vcharts.transform(spec));
        });
    });

    describe('@join', function () {
        it('should join strings', function () {
            var spec = [
                '@join', ',', ['a', 'b', 'c', 'd']
            ]
            assert.equal('a,b,c,d', vcharts.transform(spec));
        });
    });

});
<commit_msg>feat(vcharts): Testing<commit_after>var assert = require('assert'),
    vcharts = require('../src/index.js');

describe('transform', function () {
    describe('base', function () {
        it('should pass through basic structures', function () {
            assert.deepEqual({}, vcharts.transform({}));
            assert.deepEqual([], vcharts.transform([]));
            assert.deepEqual('hi', vcharts.transform('hi'));
            assert.deepEqual(1.2, vcharts.transform(1.2));
            assert.deepEqual(true, vcharts.transform(true));
            assert.deepEqual(null, vcharts.transform(null));
        });

        it('should pass through deeper objects', function () {
            var deeper = {
                a: [1, 2, 'abc'],
                b: {c: false},
                c: null
            };
            assert.deepEqual(deeper, vcharts.transform(deeper));
        });
    });

    describe('@get', function () {
        it('should lookup values', function () {
            var spec = ['@get', 'a'];
            assert.deepEqual(12, vcharts.transform(spec, {a: 12}));
        });

        it('should default values', function () {
            var spec = ['@get', 'a', 5];
            assert.deepEqual(5, vcharts.transform(spec, {b: 12}));
        });

        it('should only override undefined values', function () {
            var spec = ['@get', 'a', 5];
            assert.deepEqual(0, vcharts.transform(spec, {a: 0}));
            assert.deepEqual(false, vcharts.transform(spec, {a: false}));
            assert.deepEqual(5, vcharts.transform(spec, {a: undefined}));
        });

        it('should evaluate default values', function () {
            var spec = ['@get', 'a', ['@eq', 5, 2]];
            assert.deepEqual(false, vcharts.transform(spec, {b: 12}));
        });

        it('should not reuse default values later', function () {
            var spec = [['@get', 'a', 5], ['@get', 'a']];
            assert.deepEqual([5, null], vcharts.transform(spec, {b: 12}));
        });

        it('should not modify options', function () {
            var spec = [['@get', 'a', 5], ['@get', 'a']];
            var options = {b: 12};
            vcharts.transform(spec, options);
            assert.deepEqual({b: 12}, options);
        });
    });

    describe('@defaults', function () {
        it('should allow nested defaulting on defined parent', function () {
            var spec = [
                '@defaults',
                [['a.b', 5]],
                [['@get', 'a.b'], ['@get', 'a.d']]
            ];
            assert.deepEqual([5, 1], vcharts.transform(spec, {a: {d: 1}}));
        });

        it('should allow nested defaulting on undefined parent', function () {
            var spec = [
                '@defaults',
                [['a.b', 5]],
                ['@get', 'a.b']
            ];
            assert.deepEqual(5, vcharts.transform(spec));
        });

        it('should not override options', function () {
            var spec = [
                '@defaults',
                [['a', 5]],
                ['@get', 'a']
            ];
            assert.deepEqual(7, vcharts.transform(spec, {a: 7}));
        });

        it('should not override values from current scope', function () {
            var spec = [
                '@map',
                [1, 2, 3],
                'd',
                [
                    '@defaults',
                    [['d', 5]],
                    ['@get', 'd']
                ]
            ];
            assert.deepEqual([1, 2, 3], vcharts.transform(spec));
        });

        it('should only override undefined values', function () {
            var spec = ['@defaults', [['a', 5]], ['@get', 'a']];
            assert.deepEqual(0, vcharts.transform(spec, {a: 0}));
            assert.deepEqual(false, vcharts.transform(spec, {a: false}));
            assert.deepEqual(5, vcharts.transform(spec, {a: undefined}));
        });
    });

    describe('@map', function () {
        it('should build an array', function () {
            var spec = ['@map', [1, 2, 3], 'd', ['@get', 'd']];
            assert.deepEqual([1, 2, 3], vcharts.transform(spec));
        });

        it('should not add null array items', function () {
            var spec = ['@map', [1, null, 3], 'd', ['@get', 'd']];
            assert.deepEqual([1, 3], vcharts.transform(spec));
        });

        it('can contain complex objects', function () {
            var spec = ['@map', [1, 2, 3], 'd', {a: ['@get', 'd']}];
            assert.deepEqual([{a: 1}, {a: 2}, {a: 3}], vcharts.transform(spec));
        });

        it('can nest', function () {
            var spec = [
                '@map',
                [1, 2, 3],
                'd',
                [
                    '@map',
                    ['a', 'b'],
                    'dd',
                    {
                        d: ['@get', 'd'],
                        dd: ['@get', 'dd']
                    }
                ]
            ];
            assert.deepEqual([
                [{d: 1, dd: 'a'}, {d: 1, dd: 'b'}],
                [{d: 2, dd: 'a'}, {d: 2, dd: 'b'}],
                [{d: 3, dd: 'a'}, {d: 3, dd: 'b'}]
            ], vcharts.transform(spec));
        });

        it('should not modify options', function () {
            var spec = ['@map', [1, 2, 3], 'd', ['@get', 'd']];
            var options = {b: 12};
            vcharts.transform(spec, options);
            assert.deepEqual({b: 12}, options);
        });

        it('should override option with loop variable', function () {
            var spec = ['@map', [1, 2, 3], 'd', ['@get', 'd']];
            var options = {d: 12};
            assert.deepEqual([1, 2, 3], vcharts.transform(spec, options));
            assert.deepEqual({d: 12}, options);
        });
    });

    describe('@if', function () {
        it('should choose first option when true', function () {
            var spec = ['@if', true, 10, 20];
            assert.deepEqual(10, vcharts.transform(spec));
        });

        it('should choose second option when false', function () {
            var spec = ['@if', false, 10, 20];
            assert.deepEqual(20, vcharts.transform(spec));
        });

        it('should work with sub-expressions', function () {
            var spec = ['@if', ['@get', 'a'], ['@get', 'b'], 20];
            assert.deepEqual(5, vcharts.transform(spec, {a: true, b: 5}));
        });

        it('should treat JavaScript falsy values as false', function () {
            var spec = [
                ['@if', null, 10, 20],
                ['@if', undefined, 10, 20],
                ['@if', 0, 10, 20],
                ['@if', NaN, 10, 20],
                ['@if', '', 10, 20]
            ]
            assert.deepEqual(
                [20, 20, 20, 20, 20],
                vcharts.transform(spec)
            );
        });
    });

    describe('@eq', function () {
        it('should test for JavaScript === equality', function () {
            var spec = [
                ['@eq', {}, {}],
                ['@eq', 0, 0],
                ['@eq', 'abc', 'abc'],
                ['@eq', 1, '1'],
                ['@eq', null, null]
            ]
            assert.deepEqual(
                [false, true, true, false, true],
                vcharts.transform(spec)
            );
        });

        it('can work with sub-expressions', function () {
            var spec = [
                ['@eq', ['@get', 'a'], 10],
                ['@eq', ['@get', 'b'], ['@get', 'c']]
            ];
            assert.deepEqual(
                [true, true],
                vcharts.transform(spec, {a: 10, b: 5, c: 5})
            );
        });
    });

    describe('@min', function () {
        it('should find minimum of array', function () {
            var spec = [
                '@min', [{v: 10}, {v: 2}, {v: 3}, {v: 1}, {v: 8}], 'v'
            ]
            assert.equal(1, vcharts.transform(spec));
        });
    });

    describe('@max', function () {
        it('should find maximum of array', function () {
            var spec = [
                '@max', [{v: 10}, {v: 2}, {v: 3}, {v: 1}, {v: 8}], 'v'
            ]
            assert.equal(10, vcharts.transform(spec));
        });
    });

    describe('@join', function () {
        it('should join strings', function () {
            var spec = [
                '@join', ',', ['a', 'b', 'c', 'd']
            ]
            assert.equal('a,b,c,d', vcharts.transform(spec));
        });
    });

    describe('@orient', function () {
        it('should leave horizontal specs unchanged', function () {
            var spec = [
                '@orient',
                'horizontal',
                {x: 1, y: 2, yc: 3, width: 5}
            ];
            assert.deepEqual({x: 1, y: 2, yc: 3, width: 5}, vcharts.transform(spec));
        });

        it('should re-orient vertical specs and leave other props', function () {
            var spec = [
                '@orient',
                'vertical',
                {x: 1, y: 2, yc: 2, width: 5, hello: 10}
            ];
            assert.deepEqual({y: 1, x: 2, xc: 2, height: 5, hello: 10}, vcharts.transform(spec));
        });
    });
});
<|endoftext|>


<commit_before>'use strict'

const COLLECTION_FORMAT = {
  CSV: ',',
  SSV: ' ',
  TSV: '\t',
  PIPES: '|',
  MULTI: '&' // multi query param instances foo=bar&foo=baz
}

exports.COLLECTION_FORMAT = COLLECTION_FORMAT
exports.formatGroupData = formatGroupData
exports.getPathParams = getPathParams
exports.getFormData = getFormData
exports.castQueryParams = castQueryParams

function formatGroupData(groupSchema, groupData) {
  const paramNames = Object.keys(groupSchema.properties)
  if (!groupData) groupData = {}
  paramNames.forEach(name => {
    const paramSchema = groupSchema.properties[name]
    let val = groupData[name]
    val = parseCollectionFormat(paramSchema, val)
    val = parseBoolean(paramSchema, val)
    val = applyDefaultValue(paramSchema, val)
    groupData[name] = val
  })
  return groupData
}

function parseCollectionFormat(paramSchema, value) {
  if (paramSchema.type === 'array' && typeof value === 'string') {
    return stringValueToArray(value, paramSchema.collectionFormat || 'csv')
  }
  return value
}

function parseBoolean(paramSchema, value){
  if (paramSchema.type === 'boolean') {
    switch(`${value}`.toLowerCase().trim()) {
      case "true":
      case "1":
      case "on":
      case "yes":
      case "y":
        return true;
      default:
        return false;
    }
  }
  return value
}

function stringValueToArray(value, format) {
  const delimiter = COLLECTION_FORMAT[format.toUpperCase()] || COLLECTION_FORMAT.CSV
  return value.split(delimiter)
}

function applyDefaultValue(paramSchema, value) {
  return (value === undefined && !paramSchema.required) ? paramSchema.default : value
}

function getPathParams(req, operation) {
  const params = req.params ? req.params : req.params = {}
  if (req.app) return params // express

  // restify
  // req.params may contain a mix of more than just path parameters
  // as restify has the option to merge body and query params in here too.
  // This forces us to pull out just the ones defined in the swagger spec.
  // Note that this means we can't later determine if the client has sent
  // extra path parameters so validation around this is not possible.

  return operation.parameters
    .filter(op => op.in === 'path')
    .reduce((pathParams, op) => {
      if (params[op.name] !== undefined) {
        pathParams[op.name] = params[op.name]
      }
      return pathParams
    }, {})
}

function getFormData(req) {
  if (req.accepts('multipart/form-data') || req.accepts('application/x-www-form-urlencoded')) {
    const formData = Object.assign({}, req.body)
    if (Array.isArray(req.files)) {
      req.files.forEach(file => {
        formData[file.filename] = file
      })
    } else if (req.files && typeof req.files === 'object') {
      Object.assign(formData, req.files)
    }
    else if (req.file && typeof req.files === 'object') {
      formData[req.file.filename] = req.file
    }
    return formData
  }
  return null
}

function castQueryParams(req, groupSchemas) {
  const query = req.query ? req.query : req.query = {}
  const querySchema = groupSchemas.query || { properties: {} }
  Object.keys(query).forEach(key => {
    const propSchema = querySchema.properties[key]
    if (propSchema && (propSchema.type === 'integer' || propSchema.type === 'number')) {
      const num = Number(query[key])
      if (!isNaN(num)) query[key] = num
    }
  })
  return query
}
<commit_msg>fix linting<commit_after>'use strict'

const COLLECTION_FORMAT = {
  CSV: ',',
  SSV: ' ',
  TSV: '\t',
  PIPES: '|',
  MULTI: '&' // multi query param instances foo=bar&foo=baz
}

exports.COLLECTION_FORMAT = COLLECTION_FORMAT
exports.formatGroupData = formatGroupData
exports.getPathParams = getPathParams
exports.getFormData = getFormData
exports.castQueryParams = castQueryParams

function formatGroupData(groupSchema, groupData) {
  const paramNames = Object.keys(groupSchema.properties)
  if (!groupData) groupData = {}
  paramNames.forEach(name => {
    const paramSchema = groupSchema.properties[name]
    let val = groupData[name]
    val = parseCollectionFormat(paramSchema, val)
    val = parseBoolean(paramSchema, val)
    val = applyDefaultValue(paramSchema, val)
    groupData[name] = val
  })
  return groupData
}

function parseCollectionFormat(paramSchema, value) {
  if (paramSchema.type === 'array' && typeof value === 'string') {
    return stringValueToArray(value, paramSchema.collectionFormat || 'csv')
  }
  return value
}

function parseBoolean(paramSchema, value) {
  if (paramSchema.type === 'boolean') {
    switch(`${value}`.toLowerCase().trim()) {
      case 'true':
      case '1':
      case 'on':
      case 'yes':
      case 'y':
        return true
      default:
        return false
    }
  }
  return value
}

function stringValueToArray(value, format) {
  const delimiter = COLLECTION_FORMAT[format.toUpperCase()] || COLLECTION_FORMAT.CSV
  return value.split(delimiter)
}

function applyDefaultValue(paramSchema, value) {
  return (value === undefined && !paramSchema.required) ? paramSchema.default : value
}

function getPathParams(req, operation) {
  const params = req.params ? req.params : req.params = {}
  if (req.app) return params // express

  // restify
  // req.params may contain a mix of more than just path parameters
  // as restify has the option to merge body and query params in here too.
  // This forces us to pull out just the ones defined in the swagger spec.
  // Note that this means we can't later determine if the client has sent
  // extra path parameters so validation around this is not possible.

  return operation.parameters
    .filter(op => op.in === 'path')
    .reduce((pathParams, op) => {
      if (params[op.name] !== undefined) {
        pathParams[op.name] = params[op.name]
      }
      return pathParams
    }, {})
}

function getFormData(req) {
  if (req.accepts('multipart/form-data') || req.accepts('application/x-www-form-urlencoded')) {
    const formData = Object.assign({}, req.body)
    if (Array.isArray(req.files)) {
      req.files.forEach(file => {
        formData[file.filename] = file
      })
    } else if (req.files && typeof req.files === 'object') {
      Object.assign(formData, req.files)
    }
    else if (req.file && typeof req.files === 'object') {
      formData[req.file.filename] = req.file
    }
    return formData
  }
  return null
}

function castQueryParams(req, groupSchemas) {
  const query = req.query ? req.query : req.query = {}
  const querySchema = groupSchemas.query || { properties: {} }
  Object.keys(query).forEach(key => {
    const propSchema = querySchema.properties[key]
    if (propSchema && (propSchema.type === 'integer' || propSchema.type === 'number')) {
      const num = Number(query[key])
      if (!isNaN(num)) query[key] = num
    }
  })
  return query
}
<|endoftext|>


<commit_before>import React from "react";
import { intlShape } from "react-intl";
import { Field } from "simple-react-form";
import Geosuggest from "react-geosuggest";
import "./CaseEditor.css";
import "../GeoSuggest/GeoSuggest.css";
import {
  SimpleRelatedCases,
  SimpleRelatedMethods,
  SimpleRelatedOrganizations
} from "../RelatedEditors";
import ItemEditor from "../ItemEditor";
import FlatButton from "material-ui/FlatButton";

import {
  TextPropEditor,
  BooleanPropEditor,
  NumberPropEditor,
  DatePropEditor,
  ChoicePropEditor
} from "../PropEditors";

function CaseSidebar({
  thing,
  intl,
  cases,
  methods,
  isQuick,
  isNew,
  organizations,
  onExpand
}) {
  if (!thing) return
;
  let related_cases = (
    <Field
      fieldName="related_cases"
      name="related_cases"
      thing={thing}
      type={SimpleRelatedCases}
      property="related_cases"
      value={thing.related_cases || []}
      dataSource={cases}
      intl={intl}
    />
  );
  let related_methods = (
    <Field
      fieldName="related_methods"
      name="related_methods"
      thing={thing}
      type={SimpleRelatedMethods}
      property="related_methods"
      value={thing.related_methods || []}
      dataSource={methods}
      intl={intl}
    />
  );
  let related_organizations = (
    <Field
      fieldName="related_organizations"
      name="related_organizations"
      thing={thing}
      type={SimpleRelatedOrganizations}
      property="related_organizations"
      value={thing.related_organizations || []}
      dataSource={organizations}
      intl={intl}
    />
  );

  return (

          {intl.formatMessage({
            id: "country_picker"
          })}

        <Geosuggest />

      {isQuick
        ?

            <FlatButton onClick={onExpand} label="Do full version" />

        :

              Keywords

            keyword picker

              Related Content

                  {intl.formatMessage({ id: "cases" })}

                {related_cases}

                  {intl.formatMessage({ id: "methods" })}

                {related_methods}

                  {intl.formatMessage({ id: "organizations" })}

                {related_organizations}

              <ChoicePropEditor
                intl={intl}
                label="specific_topic"
                property="specific_topic"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="issue"
                property="issue"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="communication_mode"
                property="communication_mode"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="communication_with_audience"
                property="communication_with_audience"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="decision_method"
                property="decision_method"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="facetoface_online_or_both"
                property="facetoface_online_or_both"
                thing={thing}
              />
              <BooleanPropEditor
                intl={intl}
                label="facilitated"
                property="facilitated"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="voting"
                property="voting"
                thing={thing}
              />
              <NumberPropEditor
                intl={intl}
                label="number_of_meeting_days"
                property="number_of_meeting_days"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="targeted_participant_demographic"
                property="targeted_participant_demographic"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="kind_of_influence"
                property="kind_of_influence"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="targeted_participants_public_role"
                property="targeted_participants_public_role"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="targeted_audience"
                property="targeted_audience"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="participant_selection"
                property="participant_selection"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="type_of_funding_entity"
                property="type_of_funding_entity"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="typical_implementing_entity"
                property="typical_implementing_entity"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="typical_sponsoring_entity"
                property="typical_sponsoring_entity"
                thing={thing}
              />
              <BooleanPropEditor
                intl={intl}
                label="ongoing"
                property="ongoing"
                thing={thing}
              />
              <DatePropEditor
                intl={intl}
                label="start_date"
                property="start_date"
                thing={thing}
              />
              <DatePropEditor
                intl={intl}
                label="end_date"
                property="end_date"
                thing={thing}
              />
              <DatePropEditor
                intl={intl}
                label="updated_date"
                property="updated_date"
                thing={thing}
              />
              <NumberPropEditor
                intl={intl}
                label="total_number_of_participants"
                property="total_number_of_participants"
                thing={thing}
              />
              <TextPropEditor
                intl={intl}
                label="staff_type"
                property="staff_type"
                thing={thing}
              />
              <TextPropEditor
                intl={intl}
                label="who_else_supported_the_initiative"
                property="who_else_supported_the_initiative"
                thing={thing}
              />

}

  );
}

function CaseEditor(props) {
  let sidebar = <CaseSidebar onExpand={props.onExpand} {...props} />;
  return <ItemEditor {...props} type="case" sidebar={sidebar} />;
}

CaseEditor.propTypes = {
  intl: intlShape.isRequired
};

export default CaseEditor;
<commit_msg>fix 405 - remove updated date from editable fields for cases<commit_after>import React from "react";
import { intlShape } from "react-intl";
import { Field } from "simple-react-form";
import Geosuggest from "react-geosuggest";
import "./CaseEditor.css";
import "../GeoSuggest/GeoSuggest.css";
import {
  SimpleRelatedCases,
  SimpleRelatedMethods,
  SimpleRelatedOrganizations
} from "../RelatedEditors";
import ItemEditor from "../ItemEditor";
import FlatButton from "material-ui/FlatButton";

import {
  TextPropEditor,
  BooleanPropEditor,
  NumberPropEditor,
  DatePropEditor,
  ChoicePropEditor
} from "../PropEditors";

function CaseSidebar({
  thing,
  intl,
  cases,
  methods,
  isQuick,
  isNew,
  organizations,
  onExpand
}) {
  if (!thing) return
;
  let related_cases = (
    <Field
      fieldName="related_cases"
      name="related_cases"
      thing={thing}
      type={SimpleRelatedCases}
      property="related_cases"
      value={thing.related_cases || []}
      dataSource={cases}
      intl={intl}
    />
  );
  let related_methods = (
    <Field
      fieldName="related_methods"
      name="related_methods"
      thing={thing}
      type={SimpleRelatedMethods}
      property="related_methods"
      value={thing.related_methods || []}
      dataSource={methods}
      intl={intl}
    />
  );
  let related_organizations = (
    <Field
      fieldName="related_organizations"
      name="related_organizations"
      thing={thing}
      type={SimpleRelatedOrganizations}
      property="related_organizations"
      value={thing.related_organizations || []}
      dataSource={organizations}
      intl={intl}
    />
  );

  return (

          {intl.formatMessage({
            id: "country_picker"
          })}

        <Geosuggest />

      {isQuick
        ?

            <FlatButton onClick={onExpand} label="Do full version" />

        :

              Keywords

            keyword picker

              Related Content

                  {intl.formatMessage({ id: "cases" })}

                {related_cases}

                  {intl.formatMessage({ id: "methods" })}

                {related_methods}

                  {intl.formatMessage({ id: "organizations" })}

                {related_organizations}

              <ChoicePropEditor
                intl={intl}
                label="specific_topic"
                property="specific_topic"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="issue"
                property="issue"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="communication_mode"
                property="communication_mode"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="communication_with_audience"
                property="communication_with_audience"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="decision_method"
                property="decision_method"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="facetoface_online_or_both"
                property="facetoface_online_or_both"
                thing={thing}
              />
              <BooleanPropEditor
                intl={intl}
                label="facilitated"
                property="facilitated"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="voting"
                property="voting"
                thing={thing}
              />
              <NumberPropEditor
                intl={intl}
                label="number_of_meeting_days"
                property="number_of_meeting_days"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="targeted_participant_demographic"
                property="targeted_participant_demographic"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="kind_of_influence"
                property="kind_of_influence"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="targeted_participants_public_role"
                property="targeted_participants_public_role"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="targeted_audience"
                property="targeted_audience"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="participant_selection"
                property="participant_selection"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="type_of_funding_entity"
                property="type_of_funding_entity"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="typical_implementing_entity"
                property="typical_implementing_entity"
                thing={thing}
              />
              <ChoicePropEditor
                intl={intl}
                label="typical_sponsoring_entity"
                property="typical_sponsoring_entity"
                thing={thing}
              />
              <BooleanPropEditor
                intl={intl}
                label="ongoing"
                property="ongoing"
                thing={thing}
              />
              <DatePropEditor
                intl={intl}
                label="start_date"
                property="start_date"
                thing={thing}
              />
              <DatePropEditor
                intl={intl}
                label="end_date"
                property="end_date"
                thing={thing}
              />
              <NumberPropEditor
                intl={intl}
                label="total_number_of_participants"
                property="total_number_of_participants"
                thing={thing}
              />
              <TextPropEditor
                intl={intl}
                label="staff_type"
                property="staff_type"
                thing={thing}
              />
              <TextPropEditor
                intl={intl}
                label="who_else_supported_the_initiative"
                property="who_else_supported_the_initiative"
                thing={thing}
              />

}

  );
}

function CaseEditor(props) {
  let sidebar = <CaseSidebar onExpand={props.onExpand} {...props} />;
  return <ItemEditor {...props} type="case" sidebar={sidebar} />;
}

CaseEditor.propTypes = {
  intl: intlShape.isRequired
};

export default CaseEditor;
<|endoftext|>


<commit_before>'use strict';

var _ = require('underscore');

module.exports = {
    getArrayType : function(type)
    {
        var matches = type.match(/\[(.*?)\]/);

        if (matches === null) {
            return 'string';
        } else {
            return matches[0].substring(
                1,
                matches[0].length - 1
            );
        }
    },

    isArrayParam : function(type)
    {
        return type.substring(0, 5) === 'array';
    },

    getDefaultValuesForParams : function(params)
    {
        var defaultValues = {},
            self          = this;

        params.forEach(function(param) {
            defaultValues[param.name] = self.getDefaultValueForParam(param);
        });

        return defaultValues;
    },

    getDefaultValueForParam : function(param)
    {
        // array[hash] defaults are provided by their sub-params
        if (param.type == 'array[hash]' && param.hasOwnProperty('defaultValue')) {
            delete param.defaultValue;
        }
        if (param.hasOwnProperty('defaultValue')) {
            return param.defaultValue;
        } else if (this.isArrayParam(param.type)) {
            return [];
        } else if (param.type === 'string') {
            return '';
        } else if (param.type === 'boolean') {
            return _(param.defaultValue).isUndefined() ? true : param.defaultValue;
        } else if (param.type === 'enum') {
            return _.first(param.enumValues);
        } else if (param.type === 'hash') {
            if (! _.isArray(param.params)) {
                var message = 'Hash type parameter with no sub-parameters defined: ';

                message += '[' + param.name + ']' + ' parameter in [' + this.props.name + '] method';
                throw new Error(message);
            }

            return this.getDefaultValuesForParams(param.params);
        }

        return null;
    },

    getDefaultValueForArrayParamElement : function(param)
    {
        var clone = _.clone(param);

        clone.type = this.getArrayType(param.type);

        return this.getDefaultValueForParam(clone);
    }
};
<commit_msg>fix comparison<commit_after>'use strict';

var _ = require('underscore');

module.exports = {
    getArrayType : function(type)
    {
        var matches = type.match(/\[(.*?)\]/);

        if (matches === null) {
            return 'string';
        } else {
            return matches[0].substring(
                1,
                matches[0].length - 1
            );
        }
    },

    isArrayParam : function(type)
    {
        return type.substring(0, 5) === 'array';
    },

    getDefaultValuesForParams : function(params)
    {
        var defaultValues = {},
            self          = this;

        params.forEach(function(param) {
            defaultValues[param.name] = self.getDefaultValueForParam(param);
        });

        return defaultValues;
    },

    getDefaultValueForParam : function(param)
    {
        // array[hash] defaults are provided by their sub-params
        if (param.type === 'array[hash]' && param.hasOwnProperty('defaultValue')) {
            delete param.defaultValue;
        }
        if (param.hasOwnProperty('defaultValue')) {
            return param.defaultValue;
        } else if (this.isArrayParam(param.type)) {
            return [];
        } else if (param.type === 'string') {
            return '';
        } else if (param.type === 'boolean') {
            return _(param.defaultValue).isUndefined() ? true : param.defaultValue;
        } else if (param.type === 'enum') {
            return _.first(param.enumValues);
        } else if (param.type === 'hash') {
            if (! _.isArray(param.params)) {
                var message = 'Hash type parameter with no sub-parameters defined: ';

                message += '[' + param.name + ']' + ' parameter in [' + this.props.name + '] method';
                throw new Error(message);
            }

            return this.getDefaultValuesForParams(param.params);
        }

        return null;
    },

    getDefaultValueForArrayParamElement : function(param)
    {
        var clone = _.clone(param);

        clone.type = this.getArrayType(param.type);

        return this.getDefaultValueForParam(clone);
    }
};
<|endoftext|>


<commit_before>const serve = require('koa-static');
const Koa = require('koa');
const app = new Koa();
const path = require('path');
const makeDir = require('make-dir');
const puppeteer = require('puppeteer');
const looksSame = require('looks-same');
const test = require('ava');

const PORT = 8567;

async function createServer(port) {
  app.use(serve(path.join(__dirname, '..')));

  return new Promise((resolve, reject) => {
    const server = app.listen(port, () => resolve(server));
  });
}

async function runPuppeteer(sd) {
  const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });
  const page = await browser.newPage();

  page.on('console', (...args) => {
    //console.log.apply(console, ['[Browser]', ...args]);
    //console.log(args[0].text);
  });

  await page.goto(`http://localhost:${PORT}/tests/index.html`);

  await page.mouse.click(200, 100, { clickCount: 2, delay: 50 });

  const screenshot = path.join(sd, 'onePanel.png');

  await page.screenshot({ path: screenshot });

  return new Promise((resolve, reject) => {
    looksSame(
      screenshot,
      path.join(__dirname, '..', 'tests', 'fixtures', 'onePanel.png'),
      { tolerance: 20, ignoreCaret: true },
      (error, equal) => {
        if (error) {
          reject(error);
        } else {
          resolve(equal);
        }
      }
    );
  });

  await browser.close();
}

test('simple', async t => {
  const sd = path.join(__dirname, '..', 'build');
  await makeDir(sd);
  const server = await createServer(PORT);
  const same = await runPuppeteer(sd);
  t.is(same, true);
  server.close();
});
<commit_msg>test: code cleanup<commit_after>const serve = require('koa-static');
const Koa = require('koa');
const app = new Koa();
const { join } = require('path');
const makeDir = require('make-dir');
const puppeteer = require('puppeteer');
const looksSame = require('looks-same');
const test = require('ava');

const PORT = 8567;

async function createServer(port) {
  app.use(serve(join(__dirname, '..')));

  return new Promise((resolve, reject) => {
    const server = app.listen(port, () => resolve(server));
  });
}

async function runPuppeteer(sd) {
  const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });
  const page = await browser.newPage();

  page.on('console', (...args) => {
    //console.log.apply(console, ['[Browser]', ...args]);
    //console.log(args[0].text);
  });

  await page.goto(`http://localhost:${PORT}/tests/index.html`);

  await page.mouse.click(200, 100, { clickCount: 2, delay: 50 });

  const screenshot = join(sd, 'onePanel.png');

  await page.screenshot({ path: screenshot });

  return new Promise((resolve, reject) => {
    looksSame(
      screenshot,
      join(__dirname, '..', 'tests', 'fixtures', 'onePanel.png'),
      { tolerance: 20, ignoreCaret: true },
      (error, equal) => {
        if (error) {
          reject(error);
        } else {
          resolve(equal);
        }
      }
    );
  });

  await browser.close();
}

test('simple', async t => {
  const sd = join(__dirname, '..', 'build');
  await makeDir(sd);
  const server = await createServer(PORT);
  const same = await runPuppeteer(sd);
  t.is(same, true);
  server.close();
});
<|endoftext|>


<commit_before>var _ = require('underscore');
var format = require('../../utils/format');
var dot = require('dot');
var BaseWidget = require('./base');
var QueryBuilder = require('../filters/query_builder');

var basicsQueryTpl = dot.template([
    'basics AS (',
    '  SELECT max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val, count(1) AS total_rows',
    '  FROM ({{=it._query}}) _cdb_basics',
    ')'
].join(' \n'));

var overrideBasicsQueryTpl = dot.template([
    'basics AS (',
    '  SELECT max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val, count(1) AS total_rows',
    '  FROM ({{=it._query}}) _cdb_basics',
    ')'
].join(' \n'));

var iqrQueryTpl = dot.template([
    'iqrange AS (',
    '  SELECT max(quartile_max) - min(quartile_max) AS iqr',
    '  FROM (',
    '    SELECT quartile, max({{=it._column}}) AS quartile_max from (',
    '      SELECT {{=it._column}}, ntile(4) over (order by {{=it._column}}',
    '    ) AS quartile',
    '    FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
    '    WHERE quartile = 1 or quartile = 3',
    '    GROUP BY quartile',
    '  ) _cdb_iqr',
    ')'
].join(' \n'));

var binsQueryTpl = dot.template([
    'bins AS (',
    '  SELECT CASE WHEN total_rows = 0 OR iqr = 0 THEN 1 ELSE GREATEST(4, LEAST(cast(((max_val - min_val) / (2 * iqr * power(total_rows, 1/3))) as int), 10)) END AS bins_number',
    '  FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
    '  LIMIT 1',
    ')'
].join(' \n'));

var overrideBinsQueryTpl = dot.template([
    'bins AS (',
    '  SELECT {{=it._bins}} AS bins_number',
    ')'
].join(' \n'));

var nullsQueryTpl = dot.template([
    'nulls AS (',
    '  SELECT',
    '  count(*) AS nulls_count',
    '  FROM ({{=it._query}}) _cdb_histogram_nulls',
    '  WHERE {{=it._column}} IS NULL',
    ')'
].join(' \n'));

var histogramQueryTpl = dot.template([
    'SELECT',
    '    (max_val - min_val) / cast(bins_number as float) AS bin_width,',
    '    bins_number,',
    '    nulls_count,',
    '    CASE WHEN min_val = max_val THEN 0 ELSE width_bucket({{=it._column}}, min_val, max_val, bins_number - 1) - 1 END AS bin,',
    '    min({{=it._column}})::numeric AS min,',
    '    max({{=it._column}})::numeric AS max,',
    '    count(*) AS freq',
    'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
    'WHERE {{=it._column}} IS NOT NULL',
    'GROUP BY bin, bins_number, bin_width, nulls_count',
    'ORDER BY bin'
].join(' \n'));

var TYPE = 'histogram';

/**
 {
     type: 'histogram',
     options: {
         column: 'name',
         bins: 10 // OPTIONAL
     }
 }
 */
function Histogram(query, options) {
    if (!_.isString(options.column)) {
        throw new Error('Histogram expects `column` in widget options');
    }

    BaseWidget.apply(this);

    this.query = query;
    this.column = options.column;
    this.bins = options.bins || 10;
}

Histogram.prototype = new BaseWidget();
Histogram.prototype.constructor = Histogram;

module.exports = Histogram;

Histogram.prototype.sql = function(filters, override) {
    var _query = QueryBuilder.getSql(this.query, filters);

    var basicsQuery, binsQuery;

    if (_.has(override, 'start', 'end', 'bins')) {
        console.log('overriding with %j', override);
        basicsQuery = overrideBasicsQueryTpl({
            _query: _query,
            _start: override.start,
            _end: override.end
        });

        binsQuery = [
            overrideBinsQueryTpl({
                _bins: override.bins
            })
        ].join(',');

//        binsQuery = [
//            iqrQueryTpl({
//                _query: _query,
//                _column: this.column
//            }),
//            binsQueryTpl({
//                _query: _query
//            })
//        ].join(',');

    } else {
        basicsQuery = basicsQueryTpl({
            _query: _query,
            _column: this.column
        });

        binsQuery = [
            iqrQueryTpl({
                _query: _query,
                _column: this.column
            }),
            binsQueryTpl({
                _query: _query
            })
        ].join(',\n');
    }

    var histogramSql = [
        "WITH",
        [
            basicsQuery,
            binsQuery,
            nullsQueryTpl({
                _query: _query,
                _column: this.column
            })
        ].join(',\n'),
        histogramQueryTpl({
            _query: _query,
            _column: this.column
        })
    ].join('\n');
//
//    var query = format(histogramSql,
//        {
//            _start: override.hasOwnProperty('start') ? override.start : '{_column}',
//            _end: override.hasOwnProperty('end') ? override.end : '{_column}'
//        },
//        {
//            _query: QueryBuilder.getSql(this.query, filters),
//            _column: this.column,
//            _bins: this.bins
//        }
//    );

    console.log(histogramSql);

    return histogramSql;
};

Histogram.prototype.format = function(result, override) {
    override = override || {};
    var buckets = [];
    var nulls = 0;
    var numberOfBins;

    if (result.rows.length) {
        var firstRow = result.rows[0];
        numberOfBins = firstRow.bins_number;
        var width = firstRow.bin_width;
        nulls = firstRow.nulls_count;
        var start = override.hasOwnProperty('start') ? override.start : firstRow.min;

        buckets = new Array(numberOfBins);
        result.rows.forEach(function(row) {
            buckets[row.bin] = _.omit(row, 'bins_number', 'bin_width', 'nulls_count');
        });

        for (var i = 0; i < numberOfBins; i++) {
            var binStart = start + (i * width);
            var binEnd = start + ((i + 1) * width);
            buckets[i] = _.extend({ bin: i, start: binStart, end: binEnd, freq: 0 }, buckets[i]);
        }
    }

    return {
        bins: buckets,
        nulls: nulls
    };
};

Histogram.prototype.getResult = function(psql, filters, override, callback) {
    var self = this;
    var query = self.sql(filters, override);

    psql.query(query, function(err, result) {

        if (err) {
            return callback(err, result);
        }

        result = self.format(result, override);
        result.type = self.getType();

        return callback(null, result);

    }, true); // use read-only transaction
};

Histogram.prototype.getType = function() {
    return TYPE;
};

Histogram.prototype.toString = function() {
    return format('[{_type} widget] (column={_column}, bins={_bins}) {_query}', {
        _type: TYPE,
        _query: this.query,
        _column: this.column,
        _bins: this.bins
    });
};

/* for a categorical histogram we will do something like
 SELECT count(*) AS count,
 {_column}
 FROM ({_sql}) _cdb_histogram
 GROUP BY {_column}
 ORDER BY count
 -- DESC LIMIT 10;
 */
<commit_msg>check style<commit_after>var _ = require('underscore');
var format = require('../../utils/format');
var dot = require('dot');
var BaseWidget = require('./base');
var QueryBuilder = require('../filters/query_builder');

var basicsQueryTpl = dot.template([
    'basics AS (',
    '  SELECT max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val, count(1) AS total_rows',
    '  FROM ({{=it._query}}) _cdb_basics',
    ')'
].join(' \n'));

var overrideBasicsQueryTpl = dot.template([
    'basics AS (',
    '  SELECT max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val, count(1) AS total_rows',
    '  FROM ({{=it._query}}) _cdb_basics',
    ')'
].join(' \n'));

var iqrQueryTpl = dot.template([
    'iqrange AS (',
    '  SELECT max(quartile_max) - min(quartile_max) AS iqr',
    '  FROM (',
    '    SELECT quartile, max({{=it._column}}) AS quartile_max from (',
    '      SELECT {{=it._column}}, ntile(4) over (order by {{=it._column}}',
    '    ) AS quartile',
    '    FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
    '    WHERE quartile = 1 or quartile = 3',
    '    GROUP BY quartile',
    '  ) _cdb_iqr',
    ')'
].join(' \n'));

var binsQueryTpl = dot.template([
    'bins AS (',
    '  SELECT CASE WHEN total_rows = 0 OR iqr = 0',
    '      THEN 1',
    '      ELSE GREATEST(4, LEAST(cast(((max_val - min_val) / (2 * iqr * power(total_rows, 1/3))) as int), 10))',
    '    END AS bins_number',
    '  FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
    '  LIMIT 1',
    ')'
].join(' \n'));

var overrideBinsQueryTpl = dot.template([
    'bins AS (',
    '  SELECT {{=it._bins}} AS bins_number',
    ')'
].join(' \n'));

var nullsQueryTpl = dot.template([
    'nulls AS (',
    '  SELECT',
    '  count(*) AS nulls_count',
    '  FROM ({{=it._query}}) _cdb_histogram_nulls',
    '  WHERE {{=it._column}} IS NULL',
    ')'
].join(' \n'));

var histogramQueryTpl = dot.template([
    'SELECT',
    '    (max_val - min_val) / cast(bins_number as float) AS bin_width,',
    '    bins_number,',
    '    nulls_count,',
    '    CASE WHEN min_val = max_val',
    '      THEN 0',
    '      ELSE width_bucket({{=it._column}}, min_val, max_val, bins_number - 1) - 1',
    '    END AS bin,',
    '    min({{=it._column}})::numeric AS min,',
    '    max({{=it._column}})::numeric AS max,',
    '    count(*) AS freq',
    'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
    'WHERE {{=it._column}} IS NOT NULL',
    'GROUP BY bin, bins_number, bin_width, nulls_count',
    'ORDER BY bin'
].join(' \n'));

var TYPE = 'histogram';

/**
 {
     type: 'histogram',
     options: {
         column: 'name',
         bins: 10 // OPTIONAL
     }
 }
 */
function Histogram(query, options) {
    if (!_.isString(options.column)) {
        throw new Error('Histogram expects `column` in widget options');
    }

    BaseWidget.apply(this);

    this.query = query;
    this.column = options.column;
    this.bins = options.bins || 10;
}

Histogram.prototype = new BaseWidget();
Histogram.prototype.constructor = Histogram;

module.exports = Histogram;

Histogram.prototype.sql = function(filters, override) {
    var _query = QueryBuilder.getSql(this.query, filters);

    var basicsQuery, binsQuery;

    if (_.has(override, 'start', 'end', 'bins')) {
        console.log('overriding with %j', override);
        basicsQuery = overrideBasicsQueryTpl({
            _query: _query,
            _start: override.start,
            _end: override.end
        });

        binsQuery = [
            overrideBinsQueryTpl({
                _bins: override.bins
            })
        ].join(',');
    } else {
        basicsQuery = basicsQueryTpl({
            _query: _query,
            _column: this.column
        });

        binsQuery = [
            iqrQueryTpl({
                _query: _query,
                _column: this.column
            }),
            binsQueryTpl({
                _query: _query
            })
        ].join(',\n');
    }

    var histogramSql = [
        "WITH",
        [
            basicsQuery,
            binsQuery,
            nullsQueryTpl({
                _query: _query,
                _column: this.column
            })
        ].join(',\n'),
        histogramQueryTpl({
            _query: _query,
            _column: this.column
        })
    ].join('\n');
//
//    var query = format(histogramSql,
//        {
//            _start: override.hasOwnProperty('start') ? override.start : '{_column}',
//            _end: override.hasOwnProperty('end') ? override.end : '{_column}'
//        },
//        {
//            _query: QueryBuilder.getSql(this.query, filters),
//            _column: this.column,
//            _bins: this.bins
//        }
//    );

    console.log(histogramSql);

    return histogramSql;
};

Histogram.prototype.format = function(result, override) {
    override = override || {};
    var buckets = [];
    var nulls = 0;
    var numberOfBins;

    if (result.rows.length) {
        var firstRow = result.rows[0];
        numberOfBins = firstRow.bins_number;
        var width = firstRow.bin_width;
        nulls = firstRow.nulls_count;
        var start = override.hasOwnProperty('start') ? override.start : firstRow.min;

        buckets = new Array(numberOfBins);
        result.rows.forEach(function(row) {
            buckets[row.bin] = _.omit(row, 'bins_number', 'bin_width', 'nulls_count');
        });

        for (var i = 0; i < numberOfBins; i++) {
            var binStart = start + (i * width);
            var binEnd = start + ((i + 1) * width);
            buckets[i] = _.extend({ bin: i, start: binStart, end: binEnd, freq: 0 }, buckets[i]);
        }
    }

    return {
        bins: buckets,
        nulls: nulls
    };
};

Histogram.prototype.getResult = function(psql, filters, override, callback) {
    var self = this;
    var query = self.sql(filters, override);

    psql.query(query, function(err, result) {

        if (err) {
            return callback(err, result);
        }

        result = self.format(result, override);
        result.type = self.getType();

        return callback(null, result);

    }, true); // use read-only transaction
};

Histogram.prototype.getType = function() {
    return TYPE;
};

Histogram.prototype.toString = function() {
    return format('[{_type} widget] (column={_column}, bins={_bins}) {_query}', {
        _type: TYPE,
        _query: this.query,
        _column: this.column,
        _bins: this.bins
    });
};

/* for a categorical histogram we will do something like
 SELECT count(*) AS count,
 {_column}
 FROM ({_sql}) _cdb_histogram
 GROUP BY {_column}
 ORDER BY count
 -- DESC LIMIT 10;
 */
<|endoftext|>


<commit_before>export default {
  isoName: 'ar',
  nativeName: 'العربية',
  rtl: true,
  label: {
    clear: 'مسح',
    ok: 'حسناً',
    cancel: 'إلغاء',
    close: 'إغلاق',
    set: 'ضبط',
    select: 'تحديد',
    reset: 'إعادة ضبط',
    remove: 'حذف',
    update: 'تحديث',
    create: 'إنشاء',
    search: 'بحث',
    filter: 'ترشيح',
    refresh: 'تحديث'
  },
  date: {
    days: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
    daysShort: 'أحد_إثن_ثلا_أرب_خمي_جمع_سبت'.split('_'),
    months: 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
    monthsShort: 'ينا_فبر_مار_أبر_ماي_يون_يول_أغس_سبت_أكت_نوف_ديس'.split('_'),
    firstDayOfWeek: 6, // 0-6, 0 - Sunday, 1 Monday, ...
    format24h: false,
    pluralDay: 'أيام'
  },
  table: {
    noData: 'لا توجد بيانات',
    noResults: 'لا توجد نتائج',
    loading: 'جارٍ التحميل...',
    selectedRecords: function (rows) {
      return rows === 0
        ? 'لا توجد مُدخَلات محدّدة.'
        : rows === 1
          ? 'مُدخَلة واحدة محدّدة.'
          : rows === 2
            ? 'مُدخَلتان محدّدتان.'
            : 'عدد المُدخَلات المحدّدة ' + rows + '.'
    },
    recordsPerPage: 'عدد المُدخَلات في كل صفحة:',
    allRows: 'الكل',
    pagination: function (start, end, total) {
      return start + '-' + end + ' من ' + total
    },
    columns: 'أعمدة'
  },
  editor: {
    url: 'رابط',
    bold: 'عريض',
    italic: 'مائل',
    strikethrough: 'مشطوب',
    underline: 'مسطّر',
    unorderedList: 'قائمة غير مرتبة',
    orderedList: 'قائمة مرتبة',
    subscript: 'منخفض',
    superscript: 'مرتفع',
    hyperlink: 'رابط',
    toggleFullscreen: 'تفعيل أو إيقاف العرض في كامل الشاشة',
    quote: 'اقتباس',
    left: 'انتظام لجهة اليسار',
    center: 'توسيط',
    right: 'انتظام لجهة اليمين',
    justify: 'انتظام بالتساوي',
    print: 'طباعة',
    outdent: 'تقليل مسافة البداية',
    indent: 'زيادة مسافة البداية',
    removeFormat: 'إزالة التنسيق',
    formatting: 'تنسيق',
    fontSize: 'حجم الخط',
    align: 'انتظام',
    hr: 'إضافة خط أفقي',
    undo: 'تراجع',
    redo: 'إعادة',
    heading1: 'عنوان 1',
    heading2: 'عنوان 2',
    heading3: 'عنوان 3',
    heading4: 'عنوان 4',
    heading5: 'عنوان 5',
    heading6: 'عنوان 6',
    paragraph: 'فقرة',
    code: 'تعليمات برمجية',
    size1: 'صغير جداً',
    size2: 'صغير',
    size3: 'عادي',
    size4: 'فوق المتوسط',
    size5: 'كبير',
    size6: 'كبير جداً',
    size7: 'الأقصى',
    defaultFont: 'الخط الإفتراضي',
    viewSource: 'عرض المصدر'
  },
  tree: {
    noNodes: 'لا توجد عُقَد',
    noResults: 'لا توجد نتائج'
  }
}
<commit_msg>Update ar.js (#9790)<commit_after>export default {
  isoName: 'ar',
  nativeName: 'العربية',
  rtl: true,
  label: {
    clear: 'مسح',
    ok: 'حسناً',
    cancel: 'إلغاء',
    close: 'إغلاق',
    set: 'ضبط',
    select: 'تحديد',
    reset: 'إعادة ضبط',
    remove: 'حذف',
    update: 'تحديث',
    create: 'إنشاء',
    search: 'بحث',
    filter: 'تنقية',
    refresh: 'تحديث'
  },
  date: {
    days: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
    daysShort: 'أحد_إثن_ثلا_أرب_خمي_جمع_سبت'.split('_'),
    months: 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
    monthsShort: 'ينا_فبر_مار_أبر_ماي_يون_يول_أغس_سبت_أكت_نوف_ديس'.split('_'),
    firstDayOfWeek: 6, // 0-6, 0 - Sunday, 1 Monday, ...
    format24h: false,
    pluralDay: 'أيام'
  },
  table: {
    noData: 'لا توجد بيانات',
    noResults: 'لا توجد نتائج',
    loading: 'جارٍ التحميل...',
    selectedRecords: function (rows) {
      return rows === 0
        ? 'لا توجد مُدخَلات محدّدة.'
        : rows === 1
          ? 'مُدخَلة واحدة محدّدة.'
          : rows === 2
            ? 'مُدخَلتان محدّدتان.'
            : 'عدد المُدخَلات المحدّدة ' + rows + '.'
    },
    recordsPerPage: 'عدد المُدخَلات في كل صفحة:',
    allRows: 'الكل',
    pagination: function (start, end, total) {
      return start + '-' + end + ' من ' + total
    },
    columns: 'أعمدة'
  },
  editor: {
    url: 'رابط',
    bold: 'عريض',
    italic: 'مائل',
    strikethrough: 'مشطوب',
    underline: 'مسطّر',
    unorderedList: 'قائمة غير مرتبة',
    orderedList: 'قائمة مرتبة',
    subscript: 'منخفض',
    superscript: 'مرتفع',
    hyperlink: 'رابط',
    toggleFullscreen: 'تفعيل أو إيقاف العرض في كامل الشاشة',
    quote: 'اقتباس',
    left: 'انتظام لجهة اليسار',
    center: 'توسيط',
    right: 'انتظام لجهة اليمين',
    justify: 'انتظام بالتساوي',
    print: 'طباعة',
    outdent: 'تقليل مسافة البداية',
    indent: 'زيادة مسافة البداية',
    removeFormat: 'إزالة التنسيق',
    formatting: 'تنسيق',
    fontSize: 'حجم الخط',
    align: 'انتظام',
    hr: 'إضافة خط أفقي',
    undo: 'تراجع',
    redo: 'إعادة',
    heading1: 'عنوان 1',
    heading2: 'عنوان 2',
    heading3: 'عنوان 3',
    heading4: 'عنوان 4',
    heading5: 'عنوان 5',
    heading6: 'عنوان 6',
    paragraph: 'فقرة',
    code: 'تعليمات برمجية',
    size1: 'صغير جداً',
    size2: 'صغير',
    size3: 'عادي',
    size4: 'فوق المتوسط',
    size5: 'كبير',
    size6: 'كبير جداً',
    size7: 'الأقصى',
    defaultFont: 'الخط الإفتراضي',
    viewSource: 'عرض المصدر'
  },
  tree: {
    noNodes: 'لا توجد عُقَد',
    noResults: 'لا توجد نتائج'
  }
}
<|endoftext|>


<commit_before>import * as lib from './required';

describe('validators: required', () => {
  it('has a label', () => expect(lib.label).to.not.equal(null));
  it('has a description', () => expect(lib.description).to.not.equal(null));
  it('returns errors', () => expect(lib.type).to.equal('error'));

  describe('validate', () => {
    const fn = lib.validate,
      uri = 'domain.com/components/foo/instances/bar',
      name = 'foo';

    it('passes if no components', () => {
      expect(fn({})).to.eql([]);
    });

    it('passes if no required fields', () => {
      expect(fn({
        components: {
          [uri]: { a: null }
        },
        schemas: {
          [name]: {
            a: {
              _has: 'text'
            }
          }
        }
      })).to.eql([]);
    });

    it('passes if required field with data', () => {
      expect(fn({
        components: {
          [uri]: { a: 'hello' }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([]);
    });

    it('fails if required field with empty string', () => {
      expect(fn({
        components: {
          [uri]: { a: '' }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });

    it('fails if required field with null value', () => {
      expect(fn({
        components: {
          [uri]: { a: null }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });

    it('fails if required field with undefined value', () => {
      expect(fn({
        components: {
          [uri]: { a: undefined }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });

    it('fails if required field with empty array', () => {
      expect(fn({
        components: {
          [uri]: { a: [] }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });
  });
});
<commit_msg>added tests for empty check<commit_after>import * as lib from './required';

describe('validators: required', () => {
  it('has a label', () => expect(lib.label).to.not.equal(null));
  it('has a description', () => expect(lib.description).to.not.equal(null));
  it('returns errors', () => expect(lib.type).to.equal('error'));

  describe('validate', () => {
    const fn = lib.validate,
      uri = 'domain.com/components/foo/instances/bar',
      name = 'foo';

    it('passes if no components', () => {
      expect(fn({})).to.eql([]);
    });

    it('passes if no required fields', () => {
      expect(fn({
        components: {
          [uri]: { a: null }
        },
        schemas: {
          [name]: {
            a: {
              _has: 'text'
            }
          }
        }
      })).to.eql([]);
    });

    it('passes if required field with string', () => {
      expect(fn({
        components: {
          [uri]: { a: 'hello' }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([]);
    });

    it('passes if required field with number', () => {
      expect(fn({
        components: {
          [uri]: { a: 0 }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([]);
    });

    it('passes if required field with boolean', () => {
      expect(fn({
        components: {
          [uri]: { a: false }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([]);
    });

    it('fails if required field with empty string', () => {
      expect(fn({
        components: {
          [uri]: { a: '' }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });

    it('fails if required field with null value', () => {
      expect(fn({
        components: {
          [uri]: { a: null }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });

    it('fails if required field with undefined value', () => {
      expect(fn({
        components: {
          [uri]: { a: undefined }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });

    it('fails if required field with empty array', () => {
      expect(fn({
        components: {
          [uri]: { a: [] }
        },
        schemas: {
          [name]: {
            a: {
              _has: ['text', 'required']
            }
          }
        }
      })).to.eql([{
        uri,
        field: 'a',
        location: 'Foo » A'
      }]);
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose');
var S = require('string');
var ParamException = require('../exception/ParamException');

var BaseSchema = {};

BaseSchema.constructor = function (modelName, schema, mongodbUrl) {
    if (S(modelName).isEmpty()) {
        throw ParamException('modelName is empty');
    } else if (S(schema).isEmpty() || S(JSON.stringify(schema)).isEmpty()) {
        throw ParamException('schema is empty');
    } else {
        this._model = modelName;
        this._schema = schema;
        this._url = mongodbUrl || 'mongodb://localhost/calendar';
    }
    return this;
};

/**
 * Connect to MongoDB with Mongoose
 * @private
 */
BaseSchema._connect = function () {
    if (!this._db || this._db.connection.readyState === 0)
        this._db = mongoose.connect(this._url);
};

/**
 * Close connection with MongoDB
 * @private
 */
BaseSchema._disconnect = function () {
    if (this._db && this._db.connection.readyState === 1)
        this._db.disconnect();
};

/**
 * Find model items by conditions
 * @param conditions
 * @param callback
 */
BaseSchema.find = function (conditions, callback) {
    var self = this;
    this._connect();
    if (!this.schema) {
        this.schema = new mongoose.Schema(this._schema);
    }
    this._db.model(this._model, this.schema).find(conditions, function (err, datas) {
        if (callback) callback(err, datas);
        self._disconnect();
    })
};

/**
 * Save model
 * @param schemaData object
 */
BaseSchema.save = function (schemaData) {
    if (!this.schema) {
        this.schema = new mongoose.Schema(this._schema);
        mongoose.model(this._model, this.schema);
    }

    this._connect();
    var Model = mongoose.model(this._model);
    var model = new Model();
    for (var key in schemaData) {
        if (!schemaData.hasOwnProperty(key)) continue;
        model[key] = schemaData[key];
    }
    var self = this;
    model.save(function (err) {
        if (err) throw err;
        self._disconnect();
    });
};

module.exports = BaseSchema;
<commit_msg>Simplify `basicSchema.js`<commit_after>'use strict';

var mongoose = require('mongoose');
var S = require('string');
var ParamException = require('../exception/ParamException');

var STATE = {
    disconnected: 0,
    connected: 1,
    connecting: 2,
    disconnecting: 3
};

var BaseSchema = {};

/**
 * Constructor
 * @param modelName
 * @param schema
 * @param mongodbUrl
 * @returns {BaseSchema}
 */
BaseSchema.constructor = function (modelName, schema, mongodbUrl) {
    if (S(modelName).isEmpty()) {
        throw ParamException('modelName is empty');
    } else if (S(schema).isEmpty() || S(JSON.stringify(schema)).isEmpty()) {
        throw ParamException('schema is empty');
    } else {
        this._model = modelName;
        this._schema = schema;
        this._url = mongodbUrl || 'mongodb://localhost/calendar';
        this._db = mongoose.createConnection();
    }
    return this;
};

/**
 * Connect to MongoDB with Mongoose
 * @private
 */
BaseSchema._connect = function () {
    if (this._db.readyState === STATE.disconnected)
        this._db.open(this._url);
};

/**
 * Close connection with MongoDB
 * @private
 */
BaseSchema._disconnect = function () {
    if (this._db.readyState === STATE.connected)
        this._db.close();
};

/**
 * Find model items by conditions
 * @param conditions
 * @param callback
 */
BaseSchema.find = function (conditions, callback) {
    var self = this;
    this._connect();
    if (!this.schema) {
        this.schema = new mongoose.Schema(this._schema);
    }
    this._db.model(this._model, this.schema).find(conditions, function (err, datas) {
        if (callback) callback(err, datas);
        self._disconnect();
    })
};

/**
 * Save model
 * @param schemaData object
 */
BaseSchema.save = function (schemaData) {
    if (!this.schema) {
        this.schema = new mongoose.Schema(this._schema);
        mongoose.model(this._model, this.schema);
    }

    this._connect();
    var Model = mongoose.model(this._model);
    var model = new Model();
    for (var key in schemaData) {
        if (!schemaData.hasOwnProperty(key)) continue;
        model[key] = schemaData[key];
    }
    var self = this;
    model.save(function (err) {
        if (err) throw err;
        self._disconnect();
    });
};

module.exports = BaseSchema;
<|endoftext|>


<commit_before>import React from 'react'
import html2canvas from 'html2canvas'

const ExportList = ({layers}) => {
    /**
     *
     * @param {object} layers - The active layers
     * @param {string} format - The format used to export data from Geoserver
     */
    function exportMapData(layers, format) {
        layers.filter(layer => {
            if (layer.selected) {
                let link = document.createElement('a')
                let url = `http://apps.mprj.mp.br/geoserver/plataforma/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=${layer.layerName}&SRSNAME=EPSG:4326&outputFormat=${format}&CQL_FILTER=(BBOX(geom,${layer.bbox},%27EPSG:4326%27))&format_options=CHARSET:UTF-8`

                link.setAttribute('href', url)
                link.setAttribute('download', `${layer.name}.xlsx`)
                link.click()
            }
        })
    }

    /**
     * The function exports the map as a image using the lib HTML2Canvas.
     * It captures the document body, put on a canvas element and downloads as a jpg image file.
     */
    function exportMapImage() {
        html2canvas(document.body, {
            useCORS: true, // CORS must be active to render the base map on canvas element
			onrendered: function(canvas) {
                let link = document.createElement('a')
                let url = canvas.toDataURL('image/png')

                link.setAttribute('href', url)
                link.setAttribute('download', 'mp_em_mapas.png')
                link.click()
		  	},
		})
    }

    return (

                 exportMapImage()}>Imagem (jpg)

                 exportMapData(layers, "csv")}>Planilha (csv)

                 exportMapData(layers, "excel2007")}>Planilha (xlsx)

                 exportMapData(layers, "kml")}>Google Earth (kml)

                 exportMapData(layers, "SHAPE-ZIP")}>Shape File

    )
}

export default ExportList
<commit_msg>Add export to pdf function<commit_after>import React from 'react'
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'

const ExportList = ({layers}) => {
    /**
     *
     * @param {object} layers - The active layers
     * @param {string} format - The format used to export data from Geoserver
     */
    function exportMapData(layers, format) {
        layers.filter(layer => {
            if (layer.selected) {
                let link = document.createElement('a')
                let url = `http://apps.mprj.mp.br/geoserver/plataforma/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=${layer.layerName}&SRSNAME=EPSG:4326&outputFormat=${format}&CQL_FILTER=(BBOX(geom,${layer.bbox},%27EPSG:4326%27))&format_options=CHARSET:UTF-8`

                link.setAttribute('href', url)
                link.setAttribute('download', `${layer.name}.xlsx`)
                link.click()
            }
        })
    }

    /**
     * The function exports the map as a image using the lib HTML2Canvas.
     * It captures the document body, put on a canvas element and downloads as a jpg image file.
     */
    function exportMapImage() {
        html2canvas(document.body, {
            useCORS: true, // CORS must be active to render the base map on canvas element
			onrendered: function(canvas) {
                let link = document.createElement('a')
                let url = canvas.toDataURL('image/png')

                link.setAttribute('href', url)
                link.setAttribute('download', 'mp_em_mapas.png')
                link.click()
		  	},
		})
    }

    /**
     * The function exports the map as a image using the lib HTML2Canvas.
     * It captures the document body, put on a canvas element and downloads as a jpg image file.
     */
    function exportMapPDF() {
        html2canvas(document.body, {
            useCORS: true, // CORS must be active to render the base map on canvas element
			onrendered: function(canvas) {
                let imgData = canvas.toDataURL('image/png')
                let doc = new jsPDF('l', 'mm', 'a4')

                // Scale the canvas image of the application to an A4 landscape size
                doc.addImage(imgData, 'PNG', 0, 0, 297, 210)
                doc.save('mp_em_mapas.pdf')
		  	},
		})
    }

    return (

                 exportMapImage()}>Imagem (jpg)

                 exportMapPDF()}>Documento (pdf)

                 exportMapData(layers, "csv")}>Planilha (csv)

                 exportMapData(layers, "excel2007")}>Planilha (xlsx)

                 exportMapData(layers, "kml")}>Google Earth (kml)

                 exportMapData(layers, "SHAPE-ZIP")}>Shape File

    )
}

export default ExportList
<|endoftext|>


<commit_before>var carto = require('carto');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var step = require('step');
var torque_reference = require('torque.js').cartocss_reference;
var format = require('../../utils/format');

var sql = require('./sql');
var Renderer = require('./renderer');
var PSQLAdaptor = require('./psql_adaptor');
var PngRenderer = require('./png_renderer');

/// API: initializes the renderer, it should be called once
//
/// @param options initialization options.
///     - sqlClass: class used to access postgres, by default is PSQL
///         the class should provide the following interface
///          - constructor(params) where params should contain:
///            host, port, database, user, password.
///            the class is always constructed with dbParams passed to
///            getRender as-is
///          - query(sql, callback(err, data), readonly)
///            gets an SQL query and return a javascript object with
///            the same structure of a JSON format response from
///            CartoDB-SQL-API, for reference see
///            http://github.com/CartoDB/CartoDB-SQL-API/blob/1.8.2/doc/API.md#json
///            The 'readonly' parameter (false by default) requests
///            that running the query should not allowed to change the database.
///
function TorqueFactory(options) {
    // jshint newcap:false
    this.options = options || {};
    _.defaults(this.options, {
        sqlClass: PSQLAdaptor(PSQL)
    });
    if (this.options.sqlClass) {
        this.sqlClass = this.options.sqlClass;
    }
}

module.exports = TorqueFactory;

var formatToRenderer = {
    'torque.json': Renderer,
    'torque.bin': Renderer,
    'json.torque': Renderer,
    'bin.torque': Renderer,
    'torque.png': PngRenderer
};

TorqueFactory.prototype = {
    /// API: renderer name, use for information purposes
    name: 'torque',

    /// API: tile formats this module is able to render
    // TODO: deprecate 'json.torque' and 'bin.torque' with 1.18.0
    supported_formats: Object.keys(formatToRenderer),

    getRenderer: function(mapConfig, format, options, callback) {
        var dbParams = options.params;
        var layer = options.layer;

        if (_.isUndefined(layer)) {
            return callback(new Error("torque renderer only supports a single layer"));
        }
        if (!formatToRenderer[format]) {
            callback(new Error("format not supported: " + format));
            return;
        }
        var layerObj = mapConfig.getLayer(layer);
        if ( ! layerObj ) {
            callback(new Error("layer index is greater than number of layers"));
            return;
        }
        if ( layerObj.type !== 'torque' ) {
            callback(new Error("layer " + layer + " is not a torque layer"));
            return;
        }

        _.extend(dbParams, mapConfig.getLayerDatasource(layer));

        var pgSQL = sql(dbParams, this.sqlClass);
        fetchMapConfigAttributes(pgSQL, mapConfig, dbParams, layer, function(err, params){
            if (err) {
                callback(err);
                return;
            }
            var RendererClass = formatToRenderer[format];
            callback(null, new RendererClass(layerObj, pgSQL, params, layer));
        });
    }

};

//
// given cartocss return javascript properties
//
// @param required optional array of required properties
//
// @throw Error if required properties are not found
//
function attrsFromCartoCSS(cartocss, required) {
  var attrs_keys = {
    '-torque-frame-count': 'steps',
    '-torque-resolution': 'resolution',
    '-torque-animation-duration': 'animationDuration',
    '-torque-aggregation-function': 'countby',
    '-torque-time-attribute': 'column',
    '-torque-data-aggregation': 'data_aggregation'
  };
  carto.tree.Reference.setData(torque_reference.version.latest);
  var env = {
      benchmark: false,
      validation_data: false,
      effects: [],
      errors: [],
      error: function(e) {
        this.errors.push(e);
      }
  };
  var root = (carto.Parser(env)).parse(cartocss);
  var definitions = root.toList(env);
  var rules = getMapProperties(definitions, env);
  var attrs = {};
  for (var k in attrs_keys) {
    if (rules[k]) {
      attrs[attrs_keys[k]] = rules[k].value.toString();
    } else if ( required && required.indexOf(attrs_keys[k]) !== -1 ) {
        throw new Error("Missing required property '" + k + "' in torque layer CartoCSS");
    }
  }
  return attrs;
}

//
// get rules from Map definition
// stores errors in env.error
//
function getMapProperties(definitions, env) {
  var rules = {};
  definitions.filter(function(r) {
      return r.elements.join('') === 'Map';
  }).forEach(function(r) {
      for (var i = 0; i < r.rules.length; i++) {
          var key = r.rules[i].name;
          rules[key] = r.rules[i].ev(env);
      }
  });
  return rules;
}

//
// check layer and raise an exception is some error is found
//
// @throw Error if missing or malformed CartoCSS
//
function _checkLayerAttributes(layerConfig) {
  var cartoCSS = layerConfig.options.cartocss;
  if (!cartoCSS) {
    throw new Error("cartocss can't be undefined");
  }
  var checks = ['steps', 'resolution', 'column', 'countby'];
  return attrsFromCartoCSS(cartoCSS, checks);
}

// returns an array of errors if the mapconfig is not supported or contains errors
// errors is empty is the configuration is ok
// dbParams: host, dbname, user and pass
// layer: optional, if is specified only a layer is checked
function fetchMapConfigAttributes(sql, mapConfig, dbParams, layer_id, callback) {

  // only one layer supported
  var layer = mapConfig.getLayer(layer_id);

  if (!layer) {
    callback(new Error("mapconfig does not contain any torque layer"));
    return;
  }

  // check attrs
  var attrs;
  try {
      attrs = _checkLayerAttributes(layer);
  }  catch (e) {
      return callback(e);
  }

  // fetch layer attributes to check the query and so on is ok
  var layer_sql = layer.options.sql;
  fetchLayerAttributes(sql, layer_sql, attrs, function(err, layerAttrs) {
    if (err) {
      callback(err);
      return;
    }
    callback(null, _.extend(attrs, layerAttrs));
  });
}

function fetchLayerAttributes(sql, layer_sql, attrs, callback) {
  var is_time;

  step(

  //
  // first step, fetch the schema to know if torque colum is time column
  //
  function fetchSqlSchema() {
    var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: layer_sql });
    sql(query, this);
  },

  //
  // second step, get time bounds to calculate step
  //
  function fetchProps(err, data) {
    if (err) {
        throw err;
    }
    if ( ! data ) {
      console.log("ERROR: layer query '" + layer_sql + "' returned no data");
      throw new Error("Layer query returned no data");
    }
    if ( ! data.fields.hasOwnProperty(attrs.column) ) {
      console.log("ERROR: layer query " + layer_sql +
        " does not include time-attribute column '" + attrs.column + "'");
      throw new Error(
        "Layer query did not return the requested time-attribute column '" + attrs.column + "'");
    }
    is_time = data.fields[attrs.column].type === 'date';
    var column_conv = attrs.column;
    var max_tmpl, min_tmpl;
    if (is_time){
      max_tmpl = "date_part('epoch', max({column}))";
      min_tmpl = "date_part('epoch', min({column}))";
      column_conv = format("date_part('epoch', {column})", attrs);
    } else {
      max_tmpl = "max({column})";
      min_tmpl = "min({column})";
    }

    var max_col = format(max_tmpl, { column: attrs.column });
    var min_col = format(min_tmpl, { column: attrs.column });
    var sql_stats = " SELECT " +
        "count(*) as num_steps, " +
        "{max_col} max_date, " +
        "{min_col} min_date FROM  ({sql}) __torque_wrap_sql ";

    var query = format(sql_stats, {
      max_col: max_col,
      min_col: min_col,
      column: column_conv,
      sql: layer_sql
    });
    sql(query, this);
  },

  //
  // prepare metadata needed to render the tiles
  //
  function generateMetadata(err, data) {
    if(err) {
      if ( err.message ) {
          err.message = 'TorqueRenderer: ' + err.message;
      }
      callback(err);
      return;
    }
    data = data.rows[0];
    var step = (data.max_date - data.min_date + 1)/Math.min(attrs.steps, data.num_steps>>0);
    step = Math.abs(step) === Infinity ? 0 : step;
    callback(null, {
      start: data.min_date,
      end: data.max_date,
      step: step || 1,
      data_steps: data.num_steps >> 0,
      is_time: is_time
    });
  });
}
<commit_msg>adds torque types validator<commit_after>var carto = require('carto');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var step = require('step');
var torque_reference = require('torque.js').cartocss_reference;
var format = require('../../utils/format');

var sql = require('./sql');
var Renderer = require('./renderer');
var PSQLAdaptor = require('./psql_adaptor');
var PngRenderer = require('./png_renderer');

/// API: initializes the renderer, it should be called once
//
/// @param options initialization options.
///     - sqlClass: class used to access postgres, by default is PSQL
///         the class should provide the following interface
///          - constructor(params) where params should contain:
///            host, port, database, user, password.
///            the class is always constructed with dbParams passed to
///            getRender as-is
///          - query(sql, callback(err, data), readonly)
///            gets an SQL query and return a javascript object with
///            the same structure of a JSON format response from
///            CartoDB-SQL-API, for reference see
///            http://github.com/CartoDB/CartoDB-SQL-API/blob/1.8.2/doc/API.md#json
///            The 'readonly' parameter (false by default) requests
///            that running the query should not allowed to change the database.
///
function TorqueFactory(options) {
    // jshint newcap:false
    this.options = options || {};
    _.defaults(this.options, {
        sqlClass: PSQLAdaptor(PSQL)
    });
    if (this.options.sqlClass) {
        this.sqlClass = this.options.sqlClass;
    }
}

module.exports = TorqueFactory;

var formatToRenderer = {
    'torque.json': Renderer,
    'torque.bin': Renderer,
    'json.torque': Renderer,
    'bin.torque': Renderer,
    'torque.png': PngRenderer
};

TorqueFactory.prototype = {
    /// API: renderer name, use for information purposes
    name: 'torque',

    /// API: tile formats this module is able to render
    // TODO: deprecate 'json.torque' and 'bin.torque' with 1.18.0
    supported_formats: Object.keys(formatToRenderer),

    getRenderer: function(mapConfig, format, options, callback) {
        var dbParams = options.params;
        var layer = options.layer;

        if (_.isUndefined(layer)) {
            return callback(new Error("torque renderer only supports a single layer"));
        }
        if (!formatToRenderer[format]) {
            callback(new Error("format not supported: " + format));
            return;
        }
        var layerObj = mapConfig.getLayer(layer);
        if ( ! layerObj ) {
            callback(new Error("layer index is greater than number of layers"));
            return;
        }
        if ( layerObj.type !== 'torque' ) {
            callback(new Error("layer " + layer + " is not a torque layer"));
            return;
        }

        _.extend(dbParams, mapConfig.getLayerDatasource(layer));

        var pgSQL = sql(dbParams, this.sqlClass);
        fetchMapConfigAttributes(pgSQL, mapConfig, dbParams, layer, function(err, params){
            if (err) {
                callback(err);
                return;
            }
            var RendererClass = formatToRenderer[format];
            callback(null, new RendererClass(layerObj, pgSQL, params, layer));
        });
    }

};

//
// given cartocss return javascript properties
//
// @param required optional array of required properties
//
// @throw Error if required properties are not found
//
function attrsFromCartoCSS(cartocss, required) {
  var attrs_keys = {
    '-torque-frame-count': 'steps',
    '-torque-resolution': 'resolution',
    '-torque-animation-duration': 'animationDuration',
    '-torque-aggregation-function': 'countby',
    '-torque-time-attribute': 'column',
    '-torque-data-aggregation': 'data_aggregation'
  };
  carto.tree.Reference.setData(torque_reference.version.latest);
  var env = {
      benchmark: false,
      validation_data: false,
      effects: [],
      errors: [],
      error: function(e) {
        this.errors.push(e);
      }
  };
  var symbolizers = torque_reference.version.latest.layer;
  var root = (carto.Parser(env)).parse(cartocss);
  var definitions = root.toList(env);
  var rules = getMapProperties(definitions, env);
  var attrs = {};
  for (var k in attrs_keys) {
    if (rules[k]) {
      attrs[attrs_keys[k]] = rules[k].value.toString();
      var element = rules[k].value.value[0];
      var type = symbolizers[k].type;
      if(!checkValidType(element, type)){
          throw new Error("Unexpected type for property '" + k + "', expected " + type);
      }
    } else if ( required && required.indexOf(attrs_keys[k]) !== -1 ) {
        throw new Error("Missing required property '" + k + "' in torque layer CartoCSS");
    }

  }
  return attrs;
}

function checkValidType(e, type){
    if (type === "number" || type === "float") return typeof e.value === "number";
    else if (type === "string") return e.value !== "undefined" && typeof e.value === "string";
    else if (type === "color"){
        return typeof e.rgb !== "undefined" ||
            (e.name === "rgb" || e.name === "hsl" && e.args.length === 3) ||
            (e.name === "rgba" || e.name === "hsla" && e.args.length === 4);
    }
    else if (type.constructor === Array){
        return type.indexOf(e.value) > -1 || e.value === "linear";
    }
    else return true;
}

//
// get rules from Map definition
// stores errors in env.error
//
function getMapProperties(definitions, env) {
  var rules = {};
  definitions.filter(function(r) {
      return r.elements.join('') === 'Map';
  }).forEach(function(r) {
      for (var i = 0; i < r.rules.length; i++) {
          var key = r.rules[i].name;
          rules[key] = r.rules[i].ev(env);
      }
  });
  return rules;
}

//
// check layer and raise an exception is some error is found
//
// @throw Error if missing or malformed CartoCSS
//
function _checkLayerAttributes(layerConfig) {
  var cartoCSS = layerConfig.options.cartocss;
  if (!cartoCSS) {
    throw new Error("cartocss can't be undefined");
  }
  var checks = ['steps', 'resolution', 'column', 'countby'];
  return attrsFromCartoCSS(cartoCSS, checks);
}

// returns an array of errors if the mapconfig is not supported or contains errors
// errors is empty is the configuration is ok
// dbParams: host, dbname, user and pass
// layer: optional, if is specified only a layer is checked
function fetchMapConfigAttributes(sql, mapConfig, dbParams, layer_id, callback) {

  // only one layer supported
  var layer = mapConfig.getLayer(layer_id);

  if (!layer) {
    callback(new Error("mapconfig does not contain any torque layer"));
    return;
  }

  // check attrs
  var attrs;
  try {
      attrs = _checkLayerAttributes(layer);
  }  catch (e) {
      return callback(e);
  }

  // fetch layer attributes to check the query and so on is ok
  var layer_sql = layer.options.sql;
  fetchLayerAttributes(sql, layer_sql, attrs, function(err, layerAttrs) {
    if (err) {
      callback(err);
      return;
    }
    callback(null, _.extend(attrs, layerAttrs));
  });
}

function fetchLayerAttributes(sql, layer_sql, attrs, callback) {
  var is_time;

  step(

  //
  // first step, fetch the schema to know if torque colum is time column
  //
  function fetchSqlSchema() {
    var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: layer_sql });
    sql(query, this);
  },

  //
  // second step, get time bounds to calculate step
  //
  function fetchProps(err, data) {
    if (err) {
        throw err;
    }
    if ( ! data ) {
      console.log("ERROR: layer query '" + layer_sql + "' returned no data");
      throw new Error("Layer query returned no data");
    }
    if ( ! data.fields.hasOwnProperty(attrs.column) ) {
      console.log("ERROR: layer query " + layer_sql +
        " does not include time-attribute column '" + attrs.column + "'");
      throw new Error(
        "Layer query did not return the requested time-attribute column '" + attrs.column + "'");
    }
    is_time = data.fields[attrs.column].type === 'date';
    var column_conv = attrs.column;
    var max_tmpl, min_tmpl;
    if (is_time){
      max_tmpl = "date_part('epoch', max({column}))";
      min_tmpl = "date_part('epoch', min({column}))";
      column_conv = format("date_part('epoch', {column})", attrs);
    } else {
      max_tmpl = "max({column})";
      min_tmpl = "min({column})";
    }

    var max_col = format(max_tmpl, { column: attrs.column });
    var min_col = format(min_tmpl, { column: attrs.column });
    var sql_stats = " SELECT " +
        "count(*) as num_steps, " +
        "{max_col} max_date, " +
        "{min_col} min_date FROM  ({sql}) __torque_wrap_sql ";

    var query = format(sql_stats, {
      max_col: max_col,
      min_col: min_col,
      column: column_conv,
      sql: layer_sql
    });
    sql(query, this);
  },

  //
  // prepare metadata needed to render the tiles
  //
  function generateMetadata(err, data) {
    if(err) {
      if ( err.message ) {
          err.message = 'TorqueRenderer: ' + err.message;
      }
      callback(err);
      return;
    }
    data = data.rows[0];
    var step = (data.max_date - data.min_date + 1)/Math.min(attrs.steps, data.num_steps>>0);
    step = Math.abs(step) === Infinity ? 0 : step;
    callback(null, {
      start: data.min_date,
      end: data.max_date,
      step: step || 1,
      data_steps: data.num_steps >> 0,
      is_time: is_time
    });
  });
}
<|endoftext|>