From 2d2fa440c1ed3351ab41e2efde8e0789cb825f51 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Mon, 3 Feb 2020 17:43:30 +0000 Subject: [PATCH] Use multiple specific Osmose string requests - Only requesting the data we need (Osmose has many more issue types than iD will ever display) --- data/qa_errors.json | 27 --------- modules/services/osmose.js | 119 +++++++++++++++++++++++-------------- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/data/qa_errors.json b/data/qa_errors.json index 613c6cb5c..0d6ed3d0f 100644 --- a/data/qa_errors.json +++ b/data/qa_errors.json @@ -11,33 +11,6 @@ } }, "osmose": { - "items": [ - 0, - 1040, - 1050, - 1070, - 1150, - 1190, - 1280, - 2110, - 3040, - 3090, - 3161, - 3200, - 3220, - 3250, - 4010, - 4030, - 4080, - 5010, - 7040, - 7090, - 5010, - 5070, - 8300, - 8360, - 9010 - ], "errorIcons": { "0-1": "maki-home", "0-2": "maki-home", diff --git a/modules/services/osmose.js b/modules/services/osmose.js index 6321ff01f..d289e1d85 100644 --- a/modules/services/osmose.js +++ b/modules/services/osmose.js @@ -12,6 +12,10 @@ import { services as qaServices } from '../../data/qa_errors.json'; const tiler = utilTiler(); const dispatch = d3_dispatch('loaded'); const _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/en/api/0.3beta'; +const _osmoseItems = + qaServices.osmose.errorIcons.keys() + .map(s => s.split('-')[0]) + .reduce((unique, item) => unique.indexOf(item) !== -1 ? unique : [...unique, item], []); const _erZoom = 14; const _stringCache = {}; @@ -88,7 +92,7 @@ export default { let params = { // Tiles return a maximum # of errors // So we want to filter our request for only types iD supports - item: qaServices.osmose.items.join() + item: _osmoseItems }; // determine the needed tiles to cover the view @@ -182,60 +186,74 @@ export default { }, loadStrings(callback, locale=currentLocale) { - if (locale in _stringCache) { + const issueTypes = qaServices.osmose.errorIcons.keys(); + + if ( + locale in _stringCache + && _stringCache[locale].keys().length === issueTypes.length + ) { if (callback) callback(null, _stringCache[locale]); return; } - function format(string) { + // May be partially populated already if some requests were successful + if (!(locale in _stringCache)) { + _stringCache[locale] = {}; + } + + const format = string => { // Some strings contain markdown syntax string = string.replace(/\[((?:.|\n)+?)\]\((.+?)\)/g, '$1'); return string.replace(/`(.+?)`/g, '$1'); - } + }; - // Osmose API falls back to English strings where untranslated or if locale doesn't exist - const url = `${_osmoseUrlRoot}/items?langs=${locale}`; + // Only need to cache strings for supported issue types + // Using multiple individual item + class requests to reduce fetched data size + const allRequests = issueTypes.map(issueType => { + // No need to request data we already have + if (issueType in _stringCache[locale]) return; - d3_json(url) - .then(data => { - _stringCache[locale] = {}; + const cacheData = data => { + // Bunch of nested single value arrays of objects + const [ cat = {items:[]} ] = data.categories; + const [ item = {class:[]} ] = cat.items; + const [ cl = null ] = item.class; - for (let i = 0; i < data.categories.length; i++) { - let cat = data.categories[i]; - - for (let j = 0; j < cat.items.length; j++) { - let item = cat.items[j]; - - // TODO: Item has 'color' key with hex color code value, automatically style issue markers - - // Only need to cache strings for supported error types - // TODO: Instead, make multiple requests with filter by `item` and `class` to reduce data further - // See endpoint: https://osmose.openstreetmap.fr/en/api/0.3beta/items/X/class/X?langs=X - if (qaServices.osmose.items.indexOf(item.item) !== -1) { - for (let k = 0; k < item.class.length; k++) { - let { class: cl, item: cat } = item.class[k]; - let issueType = `${cat}-${cl}`; - let issueStrings = {}; - - // Value of root key will be null if no string exists - // If string exists, value is an object with key 'auto' for string - let { title, detail, trap, fix } = item.class[k]; - if (title) issueStrings.title = title.auto; - if (detail) issueStrings.detail = format(detail.auto); - if (trap) issueStrings.trap = format(trap.auto); - if (fix) issueStrings.fix = format(fix.auto); - - _stringCache[locale][issueType] = issueStrings; - } - } - } + // If null default value is reached, data wasn't as expected (or was empty) + if (!cl) { + /* eslint-disable no-console */ + console.log(`Osmose strings request (${issueType}) had unexpected data`); + /* eslint-enable no-console */ + return; } - if (callback) callback(null, _stringCache[locale]); - }) - .catch(err => { - if (callback) callback(err.message); - }); + // TODO: Item has 'color' key with hex color code value, automatically style issue markers + // const { item, color } = item; + + // Value of root key will be null if no string exists + // If string exists, value is an object with key 'auto' for string + const { title, detail, fix, trap } = cl; + + let issueStrings = {}; + if (title) issueStrings.title = title.auto; + if (detail) issueStrings.detail = format(detail.auto); + if (trap) issueStrings.trap = format(trap.auto); + if (fix) issueStrings.fix = format(fix.auto); + + _stringCache[locale][issueType] = issueStrings; + }; + + const [ item, cl ] = issueType.split('-'); + + // Osmose API falls back to English strings where untranslated or if locale doesn't exist + const url = `${_osmoseUrlRoot}/items/${item}/class/${cl}?langs=${locale}`; + + return jsonPromise(url, cacheData); + }); + + Promise.all(allRequests) + .then(() => { if (callback) callback(null, _stringCache[locale]); }) + .catch(err => { if (callback) callback(err); }); }, getStrings(issueType, locale=currentLocale) { @@ -313,4 +331,17 @@ export default { getClosedCounts() { return _erCache.closed; } -}; \ No newline at end of file +}; + +function jsonPromise(url, then) { + return new Promise((resolve, reject) => { + d3_json(url) + .then(data => { + then(data); + resolve(); + }) + .catch(err => { + reject(err); + }); + }); +} \ No newline at end of file