Move 'asChanges' into tagsFilter; add 'survey:date' option with automatic date to benches
This commit is contained in:
		
							parent
							
								
									fa5b92e6e1
								
							
						
					
					
						commit
						15a6794e59
					
				
					 10 changed files with 71 additions and 47 deletions
				
			
		| 
						 | 
					@ -6,7 +6,6 @@ import Constants from "../../Models/Constants";
 | 
				
			||||||
import FeatureSource from "../FeatureSource/FeatureSource";
 | 
					import FeatureSource from "../FeatureSource/FeatureSource";
 | 
				
			||||||
import {TagsFilter} from "../Tags/TagsFilter";
 | 
					import {TagsFilter} from "../Tags/TagsFilter";
 | 
				
			||||||
import {Tag} from "../Tags/Tag";
 | 
					import {Tag} from "../Tags/Tag";
 | 
				
			||||||
import {And} from "../Tags/And";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Handles all changes made to OSM.
 | 
					 * Handles all changes made to OSM.
 | 
				
			||||||
| 
						 | 
					@ -29,7 +28,9 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Adds a change to the pending changes
 | 
					     * Adds a change to the pending changes
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private static checkChange(key: string, value: string): { k: string, v: string } {
 | 
					    private static checkChange(kv: {k: string, v: string}): { k: string, v: string } {
 | 
				
			||||||
 | 
					        const key = kv.k;
 | 
				
			||||||
 | 
					        const value = kv.v;
 | 
				
			||||||
        if (key === undefined || key === null) {
 | 
					        if (key === undefined || key === null) {
 | 
				
			||||||
            console.log("Invalid key");
 | 
					            console.log("Invalid key");
 | 
				
			||||||
            return undefined;
 | 
					            return undefined;
 | 
				
			||||||
| 
						 | 
					@ -43,22 +44,19 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
            console.warn("Tag starts with or ends with a space - trimming anyway")
 | 
					            console.warn("Tag starts with or ends with a space - trimming anyway")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        key = key.trim();
 | 
					        return {k: key.trim(), v: value.trim()};
 | 
				
			||||||
        value = value.trim();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return {k: key, v: value};
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    addTag(elementId: string, tagsFilter: TagsFilter,
 | 
					    addTag(elementId: string, tagsFilter: TagsFilter,
 | 
				
			||||||
           tags?: UIEventSource<any>) {
 | 
					           tags?: UIEventSource<any>) {
 | 
				
			||||||
        const changes = this.tagToChange(tagsFilter);
 | 
					        const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId);
 | 
				
			||||||
 | 
					        const elementTags = eventSource.data;
 | 
				
			||||||
 | 
					        const changes = tagsFilter.asChange(elementTags).map(Changes.checkChange)
 | 
				
			||||||
        if (changes.length == 0) {
 | 
					        if (changes.length == 0) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId);
 | 
					 | 
				
			||||||
        const elementTags = eventSource.data;
 | 
					 | 
				
			||||||
        for (const change of changes) {
 | 
					        for (const change of changes) {
 | 
				
			||||||
            if (elementTags[change.k] !== change.v) {
 | 
					            if (elementTags[change.k] !== change.v) {
 | 
				
			||||||
                elementTags[change.k] = change.v;
 | 
					                elementTags[change.k] = change.v;
 | 
				
			||||||
| 
						 | 
					@ -132,28 +130,6 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
        return geojson;
 | 
					        return geojson;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private tagToChange(tagsFilter: TagsFilter) {
 | 
					 | 
				
			||||||
        let changes: { k: string, v: string }[] = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (tagsFilter instanceof Tag) {
 | 
					 | 
				
			||||||
            const tag = tagsFilter as Tag;
 | 
					 | 
				
			||||||
            if (typeof tag.value !== "string") {
 | 
					 | 
				
			||||||
                throw "Invalid value"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return [Changes.checkChange(tag.key, tag.value)];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (tagsFilter instanceof And) {
 | 
					 | 
				
			||||||
            const and = tagsFilter as And;
 | 
					 | 
				
			||||||
            for (const tag of and.and) {
 | 
					 | 
				
			||||||
                changes = changes.concat(this.tagToChange(tag));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return changes;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        console.log("Unsupported tagsfilter element to addTag", tagsFilter);
 | 
					 | 
				
			||||||
        throw "Unsupported tagsFilter element";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private uploadChangesWithLatestVersions(
 | 
					    private uploadChangesWithLatestVersions(
 | 
				
			||||||
        knownElements, newElements: OsmObject[], pending: { elementId: string; key: string; value: string }[]) {
 | 
					        knownElements, newElements: OsmObject[], pending: { elementId: string; key: string; value: string }[]) {
 | 
				
			||||||
        // Here, inside the continuation, we know that all 'neededIds' are loaded in 'knownElements', which maps the ids onto the elements
 | 
					        // Here, inside the continuation, we know that all 'neededIds' are loaded in 'knownElements', which maps the ids onto the elements
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,8 +46,8 @@ export class And extends TagsFilter {
 | 
				
			||||||
        return allChoices;
 | 
					        return allChoices;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    asHumanString(linkToWiki: boolean, shorten: boolean) {
 | 
					    asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
 | 
				
			||||||
        return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&");
 | 
					        return this.and.map(t => t.asHumanString(linkToWiki, shorten, properties)).join("&");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    isUsableAsAnswer(): boolean {
 | 
					    isUsableAsAnswer(): boolean {
 | 
				
			||||||
| 
						 | 
					@ -108,4 +108,12 @@ export class And extends TagsFilter {
 | 
				
			||||||
    usedKeys(): string[] {
 | 
					    usedKeys(): string[] {
 | 
				
			||||||
        return [].concat(...this.and.map(subkeys => subkeys.usedKeys()));
 | 
					        return [].concat(...this.and.map(subkeys => subkeys.usedKeys()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    asChange(properties: any): { k: string; v: string }[] {
 | 
				
			||||||
 | 
					        const result = []
 | 
				
			||||||
 | 
					        for (const tagsFilter of this.and) {
 | 
				
			||||||
 | 
					            result.push(...tagsFilter.asChange(properties))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -30,8 +30,8 @@ export class Or extends TagsFilter {
 | 
				
			||||||
        return choices;
 | 
					        return choices;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    asHumanString(linkToWiki: boolean, shorten: boolean) {
 | 
					    asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
 | 
				
			||||||
        return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|");
 | 
					        return this.or.map(t => t.asHumanString(linkToWiki, shorten, properties)).join("|");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    isUsableAsAnswer(): boolean {
 | 
					    isUsableAsAnswer(): boolean {
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,10 @@ export class Or extends TagsFilter {
 | 
				
			||||||
    usedKeys(): string[] {
 | 
					    usedKeys(): string[] {
 | 
				
			||||||
        return [].concat(...this.or.map(subkeys => subkeys.usedKeys()));
 | 
					        return [].concat(...this.or.map(subkeys => subkeys.usedKeys()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    asChange(properties: any): { k: string; v: string }[] {
 | 
				
			||||||
 | 
					       throw "Can not convert an 'or' into a changeset"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,10 +55,6 @@ export class RegexTag extends TagsFilter {
 | 
				
			||||||
        return this.invert;
 | 
					        return this.invert;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    substituteValues(tags: any): TagsFilter {
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    asHumanString() {
 | 
					    asHumanString() {
 | 
				
			||||||
        if (typeof this.key === "string") {
 | 
					        if (typeof this.key === "string") {
 | 
				
			||||||
            return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`;
 | 
					            return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`;
 | 
				
			||||||
| 
						 | 
					@ -82,4 +78,8 @@ export class RegexTag extends TagsFilter {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        throw "Key cannot be determined as it is a regex"
 | 
					        throw "Key cannot be determined as it is a regex"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    asChange(properties: any): { k: string; v: string }[] {
 | 
				
			||||||
 | 
					        throw "Cannot convert a regex-tag into a change";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -18,15 +18,15 @@ export default class SubstitutingTag implements TagsFilter {
 | 
				
			||||||
        this._value = value;
 | 
					        this._value = value;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static substituteString(template: string, dict: any) {
 | 
					    private static substituteString(template: string, dict: any): string {
 | 
				
			||||||
        for (const k in dict) {
 | 
					        for (const k in dict) {
 | 
				
			||||||
            template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
 | 
					            template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return template;
 | 
					        return template;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    asHumanString(linkToWiki: boolean, shorten: boolean) {
 | 
					    asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
 | 
				
			||||||
        return this._key + ":=" + this._value;
 | 
					        return this._key + "=" + SubstitutingTag.substituteString(this._value, properties);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    asOverpass(): string[] {
 | 
					    asOverpass(): string[] {
 | 
				
			||||||
| 
						 | 
					@ -56,4 +56,12 @@ export default class SubstitutingTag implements TagsFilter {
 | 
				
			||||||
    usedKeys(): string[] {
 | 
					    usedKeys(): string[] {
 | 
				
			||||||
        return [this._key];
 | 
					        return [this._key];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    asChange(properties: any): { k: string; v: string }[] {
 | 
				
			||||||
 | 
					        const v = SubstitutingTag.substituteString(this._value, properties);
 | 
				
			||||||
 | 
					        if (v.match(/{.*}/) !== null) {
 | 
				
			||||||
 | 
					            throw "Could not calculate all the substitutions: still have " + v
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return [{k: this._key, v: v}];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -81,4 +81,8 @@ export class Tag extends TagsFilter {
 | 
				
			||||||
    usedKeys(): string[] {
 | 
					    usedKeys(): string[] {
 | 
				
			||||||
        return [this.key];
 | 
					        return [this.key];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    asChange(properties: any): { k: string; v: string }[] {
 | 
				
			||||||
 | 
					        return [{k: this.key,  v: this.value}];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8,8 +8,14 @@ export abstract class TagsFilter {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract matchesProperties(properties: any): boolean;
 | 
					    abstract matchesProperties(properties: any): boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract asHumanString(linkToWiki: boolean, shorten: boolean);
 | 
					    abstract asHumanString(linkToWiki: boolean, shorten: boolean, properties: any);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract usedKeys(): string[];
 | 
					    abstract usedKeys(): string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts the tagsFilter into a list of key-values that should be uploaded to OSM.
 | 
				
			||||||
 | 
					     * Throws an error if not applicable
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract asChange(properties:any): {k: string, v:string}[]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -158,7 +158,7 @@ export default class SimpleAddUI extends UIElement {
 | 
				
			||||||
        let tagInfo = "";
 | 
					        let tagInfo = "";
 | 
				
			||||||
        const csCount = State.state.osmConnection.userDetails.data.csCount;
 | 
					        const csCount = State.state.osmConnection.userDetails.data.csCount;
 | 
				
			||||||
        if (csCount > Constants.userJourney.tagsVisibleAt) {
 | 
					        if (csCount > Constants.userJourney.tagsVisibleAt) {
 | 
				
			||||||
            tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true)).join("&");
 | 
					            tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true, {})).join("&");
 | 
				
			||||||
            tagInfo = `<br/>More information about the preset: ${tagInfo}`
 | 
					            tagInfo = `<br/>More information about the preset: ${tagInfo}`
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -186,7 +186,7 @@ export default class SimpleAddUI extends UIElement {
 | 
				
			||||||
                const csCount = State.state.osmConnection.userDetails.data.csCount;
 | 
					                const csCount = State.state.osmConnection.userDetails.data.csCount;
 | 
				
			||||||
                let tagInfo = undefined;
 | 
					                let tagInfo = undefined;
 | 
				
			||||||
                if (csCount > Constants.userJourney.tagsVisibleAt) {
 | 
					                if (csCount > Constants.userJourney.tagsVisibleAt) {
 | 
				
			||||||
                    const presets = preset.tags.map(t => new Combine ([t.asHumanString(false, true), " "]).SetClass("subtle break-words") )
 | 
					                    const presets = preset.tags.map(t => new Combine ([t.asHumanString(false, true, {}), " "]).SetClass("subtle break-words") )
 | 
				
			||||||
                    tagInfo = new Combine(presets)
 | 
					                    tagInfo = new Combine(presets)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const button: UIElement =
 | 
					                const button: UIElement =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,10 +86,10 @@ export default class TagRenderingQuestion extends UIElement {
 | 
				
			||||||
                        return Translations.t.general.noTagsSelected.SetClass("subtle").Render();
 | 
					                        return Translations.t.general.noTagsSelected.SetClass("subtle").Render();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    if (csCount < Constants.userJourney.tagsVisibleAndWikiLinked) {
 | 
					                    if (csCount < Constants.userJourney.tagsVisibleAndWikiLinked) {
 | 
				
			||||||
                        const tagsStr = tags.asHumanString(false, true);
 | 
					                        const tagsStr = tags.asHumanString(false, true, self._tags.data);
 | 
				
			||||||
                        return new FixedUiElement(tagsStr).SetClass("subtle").Render();
 | 
					                        return new FixedUiElement(tagsStr).SetClass("subtle").Render();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    return tags.asHumanString(true, true);
 | 
					                    return tags.asHumanString(true, true, self._tags.data);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        ).SetClass("block")
 | 
					        ).SetClass("block")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -227,6 +227,24 @@
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "question":{
 | 
				
			||||||
 | 
					        "en": "When was this bench last surveyed?"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "render": {
 | 
				
			||||||
 | 
					        "en": "This bench was last surveyed on {survey:date}"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "freeform": {
 | 
				
			||||||
 | 
					        "key": "survey:date",
 | 
				
			||||||
 | 
					        "type": "date"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "mappings": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "if": "survey:date:={_now:date}",
 | 
				
			||||||
 | 
					          "then": "Surveyed today!"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "hideUnderlayingFeaturesMinPercentage": 0,
 | 
					  "hideUnderlayingFeaturesMinPercentage": 0,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue