also check preset aliases in suspicious names validation

see #9522
This commit is contained in:
Martin Raifer
2025-03-17 13:31:26 +01:00
parent 74dc459a4a
commit 58cb5a0b80
3 changed files with 43 additions and 30 deletions
+1
View File
@@ -43,6 +43,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
#### :camera: Street-Level
* Add prev/next button to viewer for local georeferenced photos ([#10852], thanks [@0xatulpatil])
#### :white_check_mark: Validation
* The Suspicious Names validator warning now also compares the Name field to the presets aliases (in addition to the presets name) in the users language
#### :bug: Bugfixes
* Fix some direction cones not appearing on railway tracks ([#10843], thanks [@k-yle])
* Better handling of rate limited API calls and other API errors ([#10299])
+10 -9
View File
@@ -45,17 +45,18 @@ export function validationSuspiciousName(context) {
return false;
}
/** @param {string} name @param {string} presetName */
function nameMatchesPresetName(name, presetName) {
if (!presetName) return false;
/** @param {string} name */
function nameMatchesPresetName(name, preset) {
if (!preset) return false;
return name.toLowerCase() === presetName.toLowerCase();
name = name.toLowerCase();
return name === preset.name().toLowerCase() || preset.aliases().some(alias => name === alias.toLowerCase());
}
/** @param {string} name @param {string} presetName */
function isGenericName(name, tags, presetName) {
/** @param {string} name */
function isGenericName(name, tags, preset) {
name = name.toLowerCase();
return nameMatchesRawTag(name, tags) || nameMatchesPresetName(name, presetName) || isGenericMatchInNsi(tags);
return nameMatchesRawTag(name, tags) || nameMatchesPresetName(name, preset) || isGenericMatchInNsi(tags);
}
function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
@@ -113,7 +114,7 @@ export function validationSuspiciousName(context) {
let issues = [];
const presetName = presetManager.match(entity, context.graph()).name();
const preset = presetManager.match(entity, context.graph());
for (let key in tags) {
const m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
@@ -122,7 +123,7 @@ export function validationSuspiciousName(context) {
const langCode = m.length >= 2 ? m[1] : null;
const value = tags[key];
if (isGenericName(value, tags, presetName)) {
if (isGenericName(value, tags, preset)) {
issues.provisional = _waitingForNsi; // retry later if we are waiting on NSI to finish loading
issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
}
+32 -21
View File
@@ -34,7 +34,7 @@ describe('iD.validations.suspicious_name', function () {
genericWords: ['^stores?$']
};
iD.fileFetcher.cache().preset_presets = {
'Velero': { tags: { craft: 'sailmaker' }, geometry: ['line'] },
'Velero': { tags: { craft: 'sailmaker' }, aliases: ['Velaio'], geometry: ['line'] },
'Constructor de barco': { tags: { craft: 'boatbuilder' }, geometry: ['line'] },
};
});
@@ -71,57 +71,57 @@ describe('iD.validations.suspicious_name', function () {
return issues;
}
it('has no errors on init', async () => {
it('has no errors on init', () => {
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(0);
});
it('ignores way with no tags', async () => {
it('ignores way with no tags', () => {
createWay({});
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(0);
});
it('ignores feature with no name', async () => {
it('ignores feature with no name', () => {
createWay({ shop: 'supermarket' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(0);
});
it('ignores feature with a specific name', async () => {
it('ignores feature with a specific name', () => {
createWay({ shop: 'supermarket', name: 'Lou\'s' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(0);
});
it('ignores feature with a specific name that includes a generic name', async () => {
it('ignores feature with a specific name that includes a generic name', () => {
createWay({ shop: 'supermarket', name: 'Lou\'s Store' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(0);
});
it('ignores feature matching excludeNamed pattern in name-suggestion-index', async () => {
it('ignores feature matching excludeNamed pattern in name-suggestion-index', () => {
createWay({ shop: 'supermarket', name: 'famiglia cooperativa' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(0);
});
it('flags feature matching a excludeGeneric pattern in name-suggestion-index', async () => {
it('flags feature matching a excludeGeneric pattern in name-suggestion-index', () => {
createWay({ shop: 'supermarket', name: 'super mercado' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(1);
var issue = issues[0];
@@ -131,10 +131,10 @@ describe('iD.validations.suspicious_name', function () {
expect(issue.entityIds[0]).to.eql('w-1');
});
it('flags feature matching a global exclude pattern in name-suggestion-index', async () => {
it('flags feature matching a global exclude pattern in name-suggestion-index', () => {
createWay({ shop: 'supermarket', name: 'store' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(1);
var issue = issues[0];
@@ -144,10 +144,10 @@ describe('iD.validations.suspicious_name', function () {
expect(issue.entityIds[0]).to.eql('w-1');
});
it('flags feature with a name that is just a defining tag key', async () => {
it('flags feature with a name that is just a defining tag key', () => {
createWay({ amenity: 'drinking_water', name: 'Amenity' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(1);
var issue = issues[0];
@@ -157,10 +157,10 @@ describe('iD.validations.suspicious_name', function () {
expect(issue.entityIds[0]).to.eql('w-1');
});
it('flags feature with a name that is just a defining tag value', async () => {
it('flags feature with a name that is just a defining tag value', () => {
createWay({ shop: 'red_bicycle_emporium', name: 'Red Bicycle Emporium' });
var validator = iD.validationSuspiciousName(context);
await setTimeout(20);
var issues = validate(validator);
expect(issues).to.have.lengthOf(1);
var issue = issues[0];
@@ -181,6 +181,17 @@ describe('iD.validations.suspicious_name', function () {
expect(issues[0].hash).to.eql('name:ca=Velero');
});
it('flags feature with a name that matches a preset alias', async () => {
await iD.presetManager.ensureLoaded(true);
createWay({ craft: 'sailmaker', 'name:it': 'Velaio' });
const validator = iD.validationSuspiciousName(context);
const issues = validate(validator);
expect(issues).to.have.lengthOf(1);
expect(issues[0].type).to.eql('suspicious_name');
expect(issues[0].hash).to.eql('name:it=Velaio');
});
it('flags feature with a name that matches the preset name and tag name', async () => {
await iD.presetManager.ensureLoaded(true);
createWay({ craft: 'boatbuilder', 'name:mi': 'boatbuilder', name: 'cOnStRuCtOr de barco' });