Browse Source

added post-processing step to generate labels

injects label generation function for easier unit testing

moved label generation code to separate package
pull/680/head
Stephen Hess 8 years ago
parent
commit
248b1299c8
  1. 12
      helper/geojsonify.js
  2. 83
      helper/labelGenerator.js
  3. 78
      helper/labelSchema.js
  4. 23
      middleware/assignLabels.js
  5. 5
      middleware/geocodeJSON.js
  6. 1
      package.json
  7. 8
      routes/v1.js
  8. 46
      test/unit/helper/geojsonify.js
  9. 201
      test/unit/helper/labelGenerator_AUS.js
  10. 205
      test/unit/helper/labelGenerator_CAN.js
  11. 201
      test/unit/helper/labelGenerator_GBR.js
  12. 263
      test/unit/helper/labelGenerator_USA.js
  13. 200
      test/unit/helper/labelGenerator_default.js
  14. 127
      test/unit/helper/labelGenerator_examples.js
  15. 45
      test/unit/helper/labelSchema.js
  16. 107
      test/unit/middleware/assignLabels.js
  17. 8
      test/unit/run.js

12
helper/geojsonify.js

@ -46,8 +46,6 @@ function geojsonifyPlace(params, labelGenerator, place) {
addMetaData(place, output); addMetaData(place, output);
addName(place, output); addName(place, output);
addDetails(params, place, output); addDetails(params, place, output);
addLabel(place, output, labelGenerator);
// map center_point for GeoJSON to work properly // map center_point for GeoJSON to work properly
// these should not show up in the final feature properties // these should not show up in the final feature properties
@ -69,16 +67,6 @@ function addName(src, dst) {
dst.name = src.name.default; dst.name = src.name.default;
} }
/**
* Add region-specific label string
*
* @param {object} src
* @param {object} dst
*/
function addLabel(src, dst, labelGenerator) {
dst.label = labelGenerator(dst);
}
/** /**
* Add bounding box * Add bounding box
* *

83
helper/labelGenerator.js

@ -1,83 +0,0 @@
var _ = require('lodash'),
schemas = require('./labelSchema');
module.exports = function( record ){
var schema = getSchema(record.country_a);
// in virtually all cases, this will be the `name` field
var labelParts = getInitialLabel(record);
// iterate the schema
for (var field in schema) {
var valueFunction = schema[field];
labelParts.push(valueFunction(record));
}
// retain only things that are truthy
labelParts = _.compact(labelParts);
// third, dedupe and join with a comma and return
return dedupeNameAndFirstLabelElement(labelParts).join(', ');
};
function dedupeNameAndFirstLabelElement(labelParts) {
// only dedupe if a result has more than a name (the first label part)
if (labelParts.length > 1) {
// first, dedupe the name and 1st label array elements
// this is used to ensure that the `name` and first admin hierarchy elements aren't repeated
// eg - `["Lancaster", "Lancaster", "PA", "United States"]` -> `["Lancaster", "PA", "United States"]`
var deduped = _.uniq([labelParts.shift(), labelParts.shift()]);
// second, unshift the deduped parts back onto the labelParts
labelParts.unshift.apply(labelParts, deduped);
}
return labelParts;
}
function getSchema(country_a) {
if (country_a && country_a.length && schemas[country_a]) {
return schemas[country_a];
}
return schemas.default;
}
// helper function that sets a default label for non-US/CA regions and countries
function getInitialLabel(record) {
if (isRegion(record.layer) &&
isGeonamesOrWhosOnFirst(record.source) &&
isUSAOrCAN(record.country_a)) {
return [];
}
if (isCountry(record.layer)) {
return [];
}
return [record.name];
}
// this can go away once geonames is no longer supported
// https://github.com/pelias/wof-admin-lookup/issues/49
function isCountry(layer) {
return 'country' === layer;
}
function isRegion(layer) {
return 'region' === layer;
}
function isUSAOrCAN(country_a) {
return 'USA' === country_a || 'CAN' === country_a;
}
function isGeonamesOrWhosOnFirst(source) {
return 'geonames' === source || 'whosonfirst' === source;
}

78
helper/labelSchema.js

@ -1,78 +0,0 @@
var _ = require('lodash');
module.exports = {
'default': {
'local': getFirstProperty(['locality', 'localadmin']),
'country': getFirstProperty(['country'])
},
'GBR': {
'local': getFirstProperty(['locality', 'localadmin']),
'regional': getFirstProperty(['macroregion']),
'country': getFirstProperty(['country'])
},
'USA': {
'borough': getFirstProperty(['borough']),
'local': getFirstProperty(['locality', 'localadmin', 'county']),
'regional': getRegionalValue,
'country': getUSACountryValue
},
'AUS': {
'local' : getFirstProperty(['locality', 'localadmin']),
'regional' : getRegionalValue,
'country': getFirstProperty(['country'])
},
'CAN': {
'local': getFirstProperty(['locality']), // no localadmins in CAN
'regional': getRegionalValue,
'country': getFirstProperty(['country'])
}
};
// find the first field of record that has a non-empty value that's not already in labelParts
function getFirstProperty(fields) {
return function(record) {
for (var i = 0; i < fields.length; i++) {
var fieldValue = record[fields[i]];
if (!_.isEmpty(fieldValue)) {
return fieldValue;
}
}
};
}
// this function is exclusively used for figuring out which field to use for states/provinces
// 1. if a state/province is the most granular bit of info entered, the label should contain
// the full state/province name, eg: Pennsylvania, USA and Ontario, CA
// 2. otherwise, the state/province abbreviation should be used, eg: Lancaster, PA, USA and Bruce, ON, CA
// 3. if the abbreviation isn't available, use the full state/province name
function getRegionalValue(record) {
if ('region' === record.layer && record.region) {
// return full state name when state is the most granular piece of info
return record.region;
} else if (record.region_a) {
// otherwise just return the region code when available
return record.region_a;
} else if (record.region) {
// return the full name when there's no region code available
return record.region;
}
}
// this function returns the full name of a country if the result is in the
// country layer (for "United States" record). It returns the abbreviation
// otherwise (eg - Lancaster, PA, USA).
function getUSACountryValue(record) {
if ('country' === record.layer && record.country) {
return record.country;
}
return record.country_a;
}

23
middleware/assignLabels.js

@ -0,0 +1,23 @@
function setup(labelGenerator) {
function middleware(req, res, next) {
return assignLabel(req, res, next, labelGenerator);
}
return middleware;
}
function assignLabel(req, res, next, labelGenerator) {
// do nothing if there's nothing to process
if (!res || !res.body || !res.body.features) {
return next();
}
res.body.features.forEach(function (feature) {
feature.properties.label = labelGenerator(feature.properties);
});
next();
}
module.exports = setup;

5
middleware/geocodeJSON.js

@ -15,8 +15,7 @@ function setup(peliasConfig, basePath, labelGenerator) {
var opts = { var opts = {
config: peliasConfig || require('pelias-config').generate().api, config: peliasConfig || require('pelias-config').generate().api,
basePath: basePath || '/', basePath: basePath || '/'
labelGenerator: labelGenerator || require('../helper/labelGenerator')
}; };
function middleware(req, res, next) { function middleware(req, res, next) {
@ -74,7 +73,7 @@ function convertToGeocodeJSON(req, res, next, opts) {
res.body.geocoding.timestamp = new Date().getTime(); res.body.geocoding.timestamp = new Date().getTime();
// convert docs to geojson and merge with geocoding block // convert docs to geojson and merge with geocoding block
extend(res.body, geojsonify(req.clean, res.data || [], opts.labelGenerator)); extend(res.body, geojsonify(req.clean, res.data || [] ));
next(); next();
} }

1
package.json

@ -53,6 +53,7 @@
"morgan": "1.7.0", "morgan": "1.7.0",
"pelias-categories": "1.1.0", "pelias-categories": "1.1.0",
"pelias-config": "2.3.0", "pelias-config": "2.3.0",
"pelias-labels": "1.2.0",
"pelias-logger": "0.1.0", "pelias-logger": "0.1.0",
"pelias-model": "4.3.0", "pelias-model": "4.3.0",
"pelias-query": "8.8.0", "pelias-query": "8.8.0",

8
routes/v1.js

@ -46,7 +46,8 @@ var postProc = {
geocodeJSON: require('../middleware/geocodeJSON'), geocodeJSON: require('../middleware/geocodeJSON'),
sendJSON: require('../middleware/sendJSON'), sendJSON: require('../middleware/sendJSON'),
parseBoundingBox: require('../middleware/parseBBox'), parseBoundingBox: require('../middleware/parseBBox'),
normalizeParentIds: require('../middleware/normalizeParentIds') normalizeParentIds: require('../middleware/normalizeParentIds'),
assignLabels: require('../middleware/assignLabels')
}; };
/** /**
@ -88,6 +89,7 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig, base),
postProc.assignLabels(require('pelias-labels')),
postProc.sendJSON postProc.sendJSON
]), ]),
autocomplete: createRouter([ autocomplete: createRouter([
@ -102,6 +104,7 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig, base),
postProc.assignLabels(require('pelias-labels')),
postProc.sendJSON postProc.sendJSON
]), ]),
reverse: createRouter([ reverse: createRouter([
@ -119,6 +122,7 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig, base),
postProc.assignLabels(require('pelias-labels')),
postProc.sendJSON postProc.sendJSON
]), ]),
nearby: createRouter([ nearby: createRouter([
@ -136,6 +140,7 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig, base),
postProc.assignLabels(require('pelias-labels')),
postProc.sendJSON postProc.sendJSON
]), ]),
place: createRouter([ place: createRouter([
@ -147,6 +152,7 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig, base),
postProc.assignLabels(require('pelias-labels')),
postProc.sendJSON postProc.sendJSON
]), ]),
status: createRouter([ status: createRouter([

46
test/unit/helper/geojsonify.js

@ -28,12 +28,8 @@ module.exports.tests.earth = function(test, common) {
} }
}]; }];
var labelGenerator = function(doc) {
return 'label for 6295630';
};
t.doesNotThrow(function(){ t.doesNotThrow(function(){
geojsonify( {}, earth, labelGenerator ); geojsonify( {}, earth );
}); });
t.end(); t.end();
}); });
@ -147,7 +143,6 @@ module.exports.tests.geojsonify = function(test, common) {
'layer': 'layer1', 'layer': 'layer1',
'source': 'source1', 'source': 'source1',
'source_id': 'source_id_1', 'source_id': 'source_id_1',
'label': 'label for id1',
'name': '\'Round Midnight Jazz and Blues Bar', 'name': '\'Round Midnight Jazz and Blues Bar',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'United Kingdom', 'country': 'United Kingdom',
@ -183,7 +178,6 @@ module.exports.tests.geojsonify = function(test, common) {
'layer': 'layer2', 'layer': 'layer2',
'source': 'source2', 'source': 'source2',
'source_id': 'source_id_2', 'source_id': 'source_id_2',
'label': 'label for id2',
'name': 'Blues Cafe', 'name': 'Blues Cafe',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'United Kingdom', 'country': 'United Kingdom',
@ -212,7 +206,6 @@ module.exports.tests.geojsonify = function(test, common) {
'layer': 'venue', 'layer': 'venue',
'source': 'openstreetmap', 'source': 'openstreetmap',
'source_id': 'source_id_3', 'source_id': 'source_id_3',
'label': 'label for node:34633854',
'name': 'Empire State Building', 'name': 'Empire State Building',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States',
@ -232,20 +225,7 @@ module.exports.tests.geojsonify = function(test, common) {
] ]
}; };
var labelGenerator = function(doc) { var json = geojsonify( {categories: 'foo'}, input );
if (doc.id === 'id1') {
return 'label for id1';
}
if (doc.id === 'id2') {
return 'label for id2';
}
if (doc.id === 'node:34633854') {
return 'label for node:34633854';
}
};
var json = geojsonify( {categories: 'foo'}, input, labelGenerator );
t.deepEqual(json, expected, 'all docs mapped'); t.deepEqual(json, expected, 'all docs mapped');
t.end(); t.end();
@ -389,8 +369,7 @@ module.exports.tests.geojsonify = function(test, common) {
'county_gid': '102082361', 'county_gid': '102082361',
'localadmin_gid': '404521211', 'localadmin_gid': '404521211',
'locality': 'New York', 'locality': 'New York',
'locality_gid': '85977539', 'locality_gid': '85977539'
'label': 'label for 85816607'
}, },
'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588], 'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588],
'geometry': { 'geometry': {
@ -404,13 +383,7 @@ module.exports.tests.geojsonify = function(test, common) {
] ]
}; };
var labelGenerator = function(doc) { var json = geojsonify( {categories: 'foo'}, input );
if (doc.id === '85816607') {
return 'label for 85816607';
}
};
var json = geojsonify( {categories: 'foo'}, input, labelGenerator );
t.deepEqual(json, expected, 'all wanted properties exposed'); t.deepEqual(json, expected, 'all wanted properties exposed');
t.end(); t.end();
@ -455,8 +428,7 @@ module.exports.tests.categories = function (test, common) {
'source': 'whosonfirst', 'source': 'whosonfirst',
'source_id': '85816607', 'source_id': '85816607',
'name': 'East New York', 'name': 'East New York',
'category': ['government'], 'category': ['government']
'label': 'label for 85816607'
}, },
'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588], 'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588],
'geometry': { 'geometry': {
@ -470,13 +442,7 @@ module.exports.tests.categories = function (test, common) {
] ]
}; };
var labelGenerator = function(doc) { var json = geojsonify( {categories: 'foo'}, input );
if (doc.id === '85816607') {
return 'label for 85816607';
}
};
var json = geojsonify( {categories: 'foo'}, input, labelGenerator );
t.deepEqual(json, expected, 'all wanted properties exposed'); t.deepEqual(json, expected, 'all wanted properties exposed');
t.end(); t.end();

201
test/unit/helper/labelGenerator_AUS.js

@ -1,201 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.australia = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'venue name, locality name, region name, Australia');
t.end();
});
test('localadmin value should be used when locality is not available', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'venue name, localadmin name, region name, Australia');
t.end();
});
test('street', function(t) {
var doc = {
'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'house number street name, locality name, region name, Australia');
t.end();
});
test('neighbourhood', function(t) {
var doc = {
'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'neighbourhood name, locality name, region name, Australia');
t.end();
});
test('locality', function(t) {
var doc = {
'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'locality name, region name, Australia');
t.end();
});
test('localadmin', function(t) {
var doc = {
'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'localadmin name, region name, Australia');
t.end();
});
test('county', function(t) {
var doc = {
'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'county name, region name, Australia');
t.end();
});
test('macrocounty', function(t) {
var doc = {
'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'macrocounty name, region name, Australia');
t.end();
});
test('region', function(t) {
var doc = {
'name': 'region name',
'layer': 'region',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'region name, Australia');
t.end();
});
test('macroregion', function(t) {
var doc = {
'name': 'macroregion name',
'layer': 'macroregion',
'macroregion': 'macroregion name',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'macroregion name, Australia');
t.end();
});
test('country', function(t) {
var doc = {
'name': 'Australia',
'layer': 'country',
'postalcode': 'postalcode',
'country_a': 'AUS',
'country': 'Australia'
};
t.equal(generator(doc),'Australia');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (AUS): ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

205
test/unit/helper/labelGenerator_CAN.js

@ -1,205 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.canada = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'venue name, locality name, region abbr, Canada');
t.end();
});
test('street', function(t) {
var doc = {
'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'house number street name, locality name, region abbr, Canada');
t.end();
});
test('neighbourhood', function(t) {
var doc = {
'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'neighbourhood name, locality name, region abbr, Canada');
t.end();
});
test('locality', function(t) {
var doc = {
'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'locality name, region abbr, Canada');
t.end();
});
test('localadmin', function(t) {
var doc = {
'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'localadmin name, region abbr, Canada');
t.end();
});
test('county', function(t) {
var doc = {
'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'county name, region abbr, Canada');
t.end();
});
test('macrocounty', function(t) {
var doc = {
'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'macrocounty name, region abbr, Canada');
t.end();
});
test('region', function(t) {
var doc = {
'name': 'region name',
'layer': 'region',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'region name, Canada');
t.end();
});
test('macroregion', function(t) {
var doc = {
'name': 'macroregion name',
'layer': 'macroregion',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'macroregion name, Canada');
t.end();
});
test('country', function(t) {
var doc = {
'name': 'Canada',
'layer': 'country',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'Canada');
t.end();
});
test('region should be used when region_a is not available', function(t) {
var doc = {
'name': 'locality name',
'layer': 'region',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'locality name, region name, Canada', 'region should be used');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (CAN): ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

201
test/unit/helper/labelGenerator_GBR.js

@ -1,201 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.united_kingdom = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'venue name, locality name, macroregion name, United Kingdom');
t.end();
});
test('localadmin value should be used when locality is not available', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'venue name, localadmin name, macroregion name, United Kingdom');
t.end();
});
test('street', function(t) {
var doc = {
'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'house number street name, locality name, macroregion name, United Kingdom');
t.end();
});
test('neighbourhood', function(t) {
var doc = {
'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'neighbourhood name, locality name, macroregion name, United Kingdom');
t.end();
});
test('locality', function(t) {
var doc = {
'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'locality name, macroregion name, United Kingdom');
t.end();
});
test('localadmin', function(t) {
var doc = {
'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'localadmin name, macroregion name, United Kingdom');
t.end();
});
test('county', function(t) {
var doc = {
'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'county name, macroregion name, United Kingdom');
t.end();
});
test('macrocounty', function(t) {
var doc = {
'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'macrocounty name, macroregion name, United Kingdom');
t.end();
});
test('region', function(t) {
var doc = {
'name': 'region name',
'layer': 'region',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'region name, macroregion name, United Kingdom');
t.end();
});
test('macroregion', function(t) {
var doc = {
'name': 'macroregion name',
'layer': 'macroregion',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'macroregion name, United Kingdom');
t.end();
});
test('country', function(t) {
var doc = {
'name': 'United Kingdom',
'layer': 'country',
'postalcode': 'postalcode',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'United Kingdom');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (GBR): ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

263
test/unit/helper/labelGenerator_USA.js

@ -1,263 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.united_states = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'venue name, locality name, region abbr, USA');
t.end();
});
test('localadmin value should be used when there is no locality', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'venue name, localadmin name, region abbr, USA');
t.end();
});
test('county value should be used when there is no localadmin', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'venue name, county name, region abbr, USA');
t.end();
});
test('street', function(t) {
var doc = {
'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'house number street name, locality name, region abbr, USA');
t.end();
});
test('neighbourhood', function(t) {
var doc = {
'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'neighbourhood name, locality name, region abbr, USA');
t.end();
});
test('venue in borough', function(t) {
var doc = {
'name': 'venue name',
'layer': 'borough',
'neighbourhood': 'neighbourhood name',
'borough': 'borough name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'venue name, borough name, locality name, region abbr, USA');
t.end();
});
test('locality', function(t) {
var doc = {
'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'locality name, region abbr, USA');
t.end();
});
test('localadmin', function(t) {
var doc = {
'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'localadmin name, region abbr, USA');
t.end();
});
test('county', function(t) {
var doc = {
'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'county name, region abbr, USA');
t.end();
});
test('macrocounty', function(t) {
var doc = {
'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'macrocounty name, region abbr, USA');
t.end();
});
test('region', function(t) {
var doc = {
'name': 'region name',
'layer': 'region',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'region name, USA');
t.end();
});
test('macroregion', function(t) {
var doc = {
'name': 'macroregion name',
'layer': 'macroregion',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'macroregion name, USA');
t.end();
});
test('country', function(t) {
var doc = {
'name': 'United States',
'layer': 'country',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'United States');
t.end();
});
test('region should be used when region_a is not available', function(t) {
var doc = {
'name': 'locality name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'locality name, region name, USA', 'region should be used');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (USA): ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

200
test/unit/helper/labelGenerator_default.js

@ -1,200 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.default_country = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'venue name, locality name, country name');
t.end();
});
test('localadmin value should be used when locality is not available', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'venue name, localadmin name, country name');
t.end();
});
test('street', function(t) {
var doc = {
'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'house number street name, locality name, country name');
t.end();
});
test('neighbourhood', function(t) {
var doc = {
'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'neighbourhood name, locality name, country name');
t.end();
});
test('locality', function(t) {
var doc = {
'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'locality name, country name');
t.end();
});
test('localadmin', function(t) {
var doc = {
'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'localadmin name, country name');
t.end();
});
test('county', function(t) {
var doc = {
'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'county name, country name');
t.end();
});
test('macrocounty', function(t) {
var doc = {
'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'macrocounty name, country name');
t.end();
});
test('region', function(t) {
var doc = {
'name': 'region name',
'layer': 'region',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'region name, country name');
t.end();
});
test('macroregion', function(t) {
var doc = {
'name': 'macroregion name',
'layer': 'macroregion',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
};
t.equal(generator(doc),'macroregion name, country name');
t.end();
});
test('country layer labels should only use the `country` field and not the `name`', function(t) {
var doc = {
'name': 'source country name',
'layer': 'country',
'country_a': 'country code',
'country': 'hierarchy country name'
};
t.equal(generator(doc),'hierarchy country name');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator: ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

127
test/unit/helper/labelGenerator_examples.js

@ -1,127 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.canada = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'Tim Horton\'s',
'layer': 'venue',
'housenumber': '1',
'street': 'Main St',
'neighbourhood': 'College Heights',
'locality': 'Thunder Bay',
'region_a': 'ON',
'region': 'Ontario',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'Tim Horton\'s, Thunder Bay, ON, Canada');
t.end();
});
test('address', function(t) {
var doc = {
'name': '1 Main St',
'layer': 'venue',
'housenumber': '1',
'street': 'Main St',
'locality': 'Truth or Consequences',
'region_a': 'NM',
'region': 'New Mexico',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'1 Main St, Truth or Consequences, NM, USA');
t.end();
});
};
module.exports.tests.france = function(test, common) {
test('eiffel tower', function(t) {
var doc = {
'name': 'Tour Eiffel',
'layer': 'venue',
'neighbourhood': 'Quartier du Gros-Caillou',
'locality': 'Paris',
'region': 'Paris',
'country_a': 'FRA',
'country': 'France'
};
t.equal(generator(doc),'Tour Eiffel, Paris, France');
t.end();
});
test('France street address', function(t) {
var doc = {
'name': '74 rue de rivoli',
'layer': 'address',
'housenumber': '74',
'street': 'Rue de Rivoli',
'neighbourhood': 'Quartier Saint-Merri',
'locality': 'Paris',
'region': 'Paris',
'country_a': 'FRA',
'country': 'France'
};
t.equal(generator(doc),'74 rue de rivoli, Paris, France');
t.end();
});
test('France neighbourhood', function(t) {
var doc = {
'name': 'Grange aux Belles Terrage',
'layer': 'neighbourhood',
'neighbourhood': 'Grange aux Belles Terrage',
'locality': 'Paris',
'region': 'Paris',
'country_a': 'FRA',
'country': 'France'
};
t.equal(generator(doc),'Grange aux Belles Terrage, Paris, France');
t.end();
});
test('Luxembourg (the city) in Luxembourg', function(t) {
var doc = {
'name': 'Luxembourg',
'layer': 'locality',
'locality': 'Luxembourg',
'country_a': 'LUX',
'country': 'Luxembourg'
};
// console.error(generator(doc));
t.equal(generator(doc),'Luxembourg, Luxembourg');
t.end();
});
};
module.exports.tests.name_only = function(test, common) {
test('name-only results (no admin fields) should not include extraneous comma', function(t) {
var doc = {
'name': 'Result name',
};
t.equal(generator(doc),'Result name');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (CAN): ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

45
test/unit/helper/labelSchema.js

@ -1,45 +0,0 @@
var schemas = require('../../../helper/labelSchema');
var alpha3 = require('../mock/alpha3.json');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof schemas, 'object', 'valid object');
t.equal(schemas.hasOwnProperty('default'), true, 'has default defined');
t.end();
});
};
module.exports.tests.supported_countries = function(test, common) {
test('supported countries', function(t) {
var supported_countries = Object.keys(schemas);
t.notEquals(supported_countries.indexOf('USA'), -1);
t.notEquals(supported_countries.indexOf('CAN'), -1);
t.notEquals(supported_countries.indexOf('GBR'), -1);
t.notEquals(supported_countries.indexOf('AUS'), -1);
t.notEquals(supported_countries.indexOf('default'), -1);
t.equals(supported_countries.length, 5);
t.equals(Object.keys(schemas.USA).length, 4);
t.equals(Object.keys(schemas.CAN).length, 3);
t.equals(Object.keys(schemas.GBR).length, 3);
t.equals(Object.keys(schemas.AUS).length, 3);
t.equals(Object.keys(schemas.default).length, 2);
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('schemas: ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

107
test/unit/middleware/assignLabels.js

@ -0,0 +1,107 @@
module.exports.tests = {};
module.exports.tests.serialization = function(test, common) {
test('undefined res should not throw an exception', function(t) {
var assignLabels = require('../../../middleware/assignLabels')(function(){});
function testIt() {
assignLabels(undefined, {}, function() {});
}
t.doesNotThrow(testIt, 'an exception should not have been thrown');
t.end();
});
test('res without body should not throw an exception', function(t) {
var assignLabels = require('../../../middleware/assignLabels')(function(){});
function testIt() {
assignLabels({}, {}, function() {});
}
t.doesNotThrow(testIt, 'an exception should not have been thrown');
t.end();
});
test('res.body without features should not throw an exception', function(t) {
var assignLabels = require('../../../middleware/assignLabels')(function(){});
function testIt() {
assignLabels({ body: {} }, {}, function() {});
}
t.doesNotThrow(testIt, 'an exception should not have been thrown');
t.end();
});
test('labels should be assigned to all results', function(t) {
var labelGenerator = function(properties) {
if (properties.id === 1) {
return 'label 1';
}
if (properties.id === 2) {
return 'label 2';
}
};
var assignLabels = require('../../../middleware/assignLabels')(labelGenerator);
var input = {
body: {
features: [
{
properties: {
id: 1
}
},
{
properties: {
id: 2
}
}
]
}
};
var expected = {
body: {
features: [
{
properties: {
id: 1,
label: 'label 1'
}
},
{
properties: {
id: 2,
label: 'label 2'
}
}
]
}
};
assignLabels({}, input, function () {
t.deepEqual(input, expected);
t.end();
});
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('[middleware] assignLabels: ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

8
test/unit/run.js

@ -14,18 +14,12 @@ var tests = [
require('./controller/search'), require('./controller/search'),
require('./helper/diffPlaces'), require('./helper/diffPlaces'),
require('./helper/geojsonify'), require('./helper/geojsonify'),
require('./helper/labelGenerator_examples'),
require('./helper/labelGenerator_default'),
require('./helper/labelGenerator_CAN'),
require('./helper/labelGenerator_AUS'),
require('./helper/labelGenerator_GBR'),
require('./helper/labelGenerator_USA'),
require('./helper/labelSchema'),
require('./helper/logging'), require('./helper/logging'),
require('./helper/type_mapping'), require('./helper/type_mapping'),
require('./helper/sizeCalculator'), require('./helper/sizeCalculator'),
require('./middleware/access_log'), require('./middleware/access_log'),
require('./middleware/accuracy'), require('./middleware/accuracy'),
require('./middleware/assignLabels'),
require('./middleware/confidenceScore'), require('./middleware/confidenceScore'),
require('./middleware/confidenceScoreFallback'), require('./middleware/confidenceScoreFallback'),
require('./middleware/confidenceScoreReverse'), require('./middleware/confidenceScoreReverse'),

Loading…
Cancel
Save