Feature(inspector): allow to load multiple contributors at once

This commit is contained in:
Pieter Vander Vennet 2024-12-04 18:48:05 +01:00
parent 7c6224fd3e
commit 063a912c82
8 changed files with 69 additions and 41 deletions

View file

@ -52,8 +52,11 @@
"render": 0,
"mappings": [
{
"if": {"or":
["_geometry:type=Polygon","_geometry:type=MultiPolygon"]
"if": {
"or": [
"_geometry:type=Polygon",
"_geometry:type=MultiPolygon"
]
},
"then": 20
}

View file

@ -1 +1,2 @@
{}
{
}

View file

@ -5,11 +5,9 @@
import OsmObjectDownloader from "../../Logic/Osm/OsmObjectDownloader"
import { OsmObject } from "../../Logic/Osm/OsmObject"
import Loading from "../Base/Loading.svelte"
import AttributedImage from "../Image/AttributedImage.svelte"
import AttributedPanoramaxImage from "./AttributedPanoramaxImage.svelte"
import History from "./History.svelte"
export let onlyShowUsername: string
export let onlyShowUsername: string[]
export let features: Feature[]
const downloader = new OsmObjectDownloader()
@ -23,11 +21,12 @@
}
return result
}))
let usernamesSet = new Set(onlyShowUsername)
let allDiffs: Store<{
key: string;
value?: string;
oldValue?: string
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, onlyShowUsername))
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, usernamesSet))
let addedImages = allDiffs.mapD(diffs => [].concat(...diffs.filter(({ key }) => imageKeys.has(key))))
@ -37,7 +36,11 @@
{:else if $addedImages.length === 0}
No images added by this contributor
{:else}
{#each $addedImages as imgDiff}
<AttributedPanoramaxImage hash={imgDiff.value} />
{/each}
<div class="flex">
{#each $addedImages as imgDiff}
<div class="w-48 h-48">
<AttributedPanoramaxImage hash={imgDiff.value} />
</div>
{/each}
</div>
{/if}

View file

@ -6,14 +6,16 @@
import Loading from "../Base/Loading.svelte"
import { HistoryUtils } from "./HistoryUtils"
import * as shared_questions from "../../assets/generated/layers/questions.json"
import TagRenderingQuestion from "../Popup/TagRendering/TagRenderingQuestion.svelte"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import Tr from "../Base/Tr.svelte"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
import Translations from "../i18n/Translations"
export let onlyShowUsername: string
export let onlyShowUsername: string[]
export let features: Feature[]
let usernames = new Set(onlyShowUsername)
const downloader = new OsmObjectDownloader()
let allHistories: UIEventSource<OsmObject[][]> = UIEventSource.FromPromise(
Promise.all(features.map(f => downloader.downloadHistory(f.properties.id)))
@ -22,7 +24,7 @@
key: string;
value?: string;
oldValue?: string
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, onlyShowUsername))
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, usernames))
const trs = shared_questions.tagRenderings.map(tr => new TagRenderingConfig(tr))
@ -69,6 +71,7 @@
return perKey
})
const t = Translations.t.inspector
</script>
@ -85,8 +88,7 @@
</h3>
<AccordionSingle>
<span slot="header">
Answered {diff.count} times
<Tr t={t.answeredCountTimes.Subs(diff)} />
</span>
<ul>
{#each diff.values as value}

View file

@ -9,10 +9,12 @@
import { HistoryUtils } from "./HistoryUtils"
import ToSvelte from "../Base/ToSvelte.svelte"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
export let onlyShowChangesBy: string
export let onlyShowChangesBy: string[]
export let id: OsmId
let usernames = new Set(onlyShowChangesBy)
let fullHistory = UIEventSource.FromPromise(new OsmObjectDownloader().downloadHistory(id))
let partOfLayer = fullHistory.mapD(history => history.map(step => ({
@ -21,11 +23,11 @@
})))
let filteredHistory = partOfLayer.mapD(history =>
history.filter(({ step }) => {
if (!onlyShowChangesBy) {
if (usernames.size == 0) {
return true
}
console.log("Comparing ", step.tags["_last_edit:contributor"], onlyShowChangesBy, step.tags["_last_edit:contributor"] === onlyShowChangesBy)
return step.tags["_last_edit:contributor"] === onlyShowChangesBy
console.log("Checking if ", step.tags["_last_edit:contributor"],"is contained in", onlyShowChangesBy)
return usernames.has(step.tags["_last_edit:contributor"])
}).map(({ step, layer }) => {
const diff = HistoryUtils.tagHistoryDiff(step, fullHistory.data)
@ -38,6 +40,8 @@
* These layers are only shown if there are tag changes as well
*/
const ignoreLayersIfNoChanges: ReadonlySet<string> = new Set(["walls_and_buildings"])
const t = Translations.t.inspector.previousContributors
</script>
{#if !$allGeometry || !ignoreLayersIfNoChanges.has($lastStep?.layer?.id)}
@ -55,7 +59,7 @@
{#if !$filteredHistory}
<Loading>Loading history...</Loading>
{:else if $filteredHistory.length === 0}
Only geometry changes found
<Tr t={t.onlyGeometry} />
{:else}
<table class="w-full m-1">
{#each $filteredHistory as { step, layer }}
@ -64,7 +68,7 @@
<tr>
<td colspan="3">
<h3>
Created by {step.tags["_last_edit:contributor"]}
<Tr t={t.createdBy.Subs({contributor: step.tags["_last_edit:contributor"]})} />
</h3>
</td>
</tr>
@ -72,7 +76,7 @@
{#if HistoryUtils.tagHistoryDiff(step, $fullHistory).length === 0}
<tr>
<td class="font-bold justify-center flex w-full" colspan="3">
Only changes in geometry
<Tr t={t.onlyGeometry} />
</td>
</tr>
{:else}

View file

@ -33,10 +33,10 @@ export class HistoryUtils {
}).filter(ch => ch.oldValue !== ch.value)
}
public static fullHistoryDiff(histories: OsmObject[][], onlyShowUsername?: string){
public static fullHistoryDiff(histories: OsmObject[][], onlyShowUsername?: Set<string>){
const allDiffs: {key: string, oldValue?: string, value?: string}[] = [].concat(...histories.map(
history => {
const filtered = history.filter(step => !onlyShowUsername || step.tags["_last_edit:contributor"] === onlyShowUsername)
const filtered = history.filter(step => !onlyShowUsername || onlyShowUsername?.has(step.tags["_last_edit:contributor"] ))
const diffs: {
key: string;
value?: string;

View file

@ -88,7 +88,12 @@
No labels
{:else}
{#each $labels as label}
<div class="mx-2">{label} </div>
<div class="mx-2">{label}
<button class:disabled={!$inspectedContributors.some(c => c.label === label)} on:click={() => {dispatch("selectUser",
inspectedContributors.data.filter(c =>c.label === label).map(c => c .name).join(";")
)}}>See all changes for these users
</button>
</div>
{/each}
{/if}
<div class="interactive flex m-2 items-center gap-x-2 rounded-lg p-2">

View file

@ -27,6 +27,8 @@
import PreviouslySpiedUsers from "./History/PreviouslySpiedUsers.svelte"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import MagnifyingGlassCircle from "@babeard/svelte-heroicons/outline/MagnifyingGlassCircle"
import Translations from "./i18n/Translations"
import Tr from "./Base/Tr.svelte"
let username = QueryParameters.GetQueryParameter("user", undefined, "Inspect this user")
let step = new UIEventSource<"waiting" | "loading" | "done">("waiting")
@ -52,10 +54,12 @@
lon.set(l.lon)
})
let allLayers = HistoryUtils.personalTheme.layers
let layersNoFixme = allLayers.filter(l => l.id !== "fixme")
let fixme = allLayers.find(l => l.id === "fixme")
let featuresStore = new UIEventSource<Feature[]>([])
let features = new StaticFeatureSource(featuresStore)
ShowDataLayer.showMultipleLayers(map, features, HistoryUtils.personalTheme.layers, {
ShowDataLayer.showMultipleLayers(map, features, [...layersNoFixme, fixme] , {
zoomToFeatures: true,
onClick: (f: Feature) => {
selectedElement.set(undefined)
@ -75,7 +79,7 @@
async function load() {
const user = username.data
{
if(user.indexOf(";")<0){
const inspectedData = inspectedContributors.data
const previousEntry = inspectedData.find(e => e.name === user)
@ -93,7 +97,7 @@
step.setData("loading")
featuresStore.set([])
const overpass = new Overpass(undefined, ["nw(user_touched:\"" + user + "\");"], Constants.defaultOverpassUrls[0])
const overpass = new Overpass(undefined, user.split(";").map(user => "nw(user_touched:\"" + user + "\");"), Constants.defaultOverpassUrls[0])
if (!maplibremap.bounds.data) {
return
}
@ -118,38 +122,44 @@
let mode: "map" | "table" | "aggregate" | "images" = "map"
let showPreviouslyVisited = new UIEventSource(true)
const t = Translations.t.inspector
</script>
<div class="flex flex-col w-full h-full">
<div class="flex gap-x-2 items-center low-interaction p-2">
<MagnifyingGlassCircle class="w-12 h-12"/>
<h1 class="flex-shrink-0 m-0 mx-2">Inspect contributor</h1>
<h1 class="flex-shrink-0 m-0 mx-2">
<Tr t={t.title}/>
</h1>
<ValidatedInput type="string" value={username} on:submit={() => load()} />
{#if loadingData}
<Loading />
{:else}
<button class="primary" on:click={() => load()}>Load</button>
<button class="primary" on:click={() => load()}>
<Tr t={t.load}/>
</button>
{/if}
<button on:click={() => showPreviouslyVisited.setData(true)}>
Show earlier inspected contributors
<Tr t={t.earlierInspected}/>
</button>
<a href="./index.html" class="button">Back to index</a>
<a href="./index.html" class="button">
<Tr t={t.backToIndex}/>
</a>
</div>
<div class="flex">
<button class:primary={mode === "map"} on:click={() => mode = "map"}>
Map view
<Tr t={t.mapView}/>
</button>
<button class:primary={mode === "table"} on:click={() => mode = "table"}>
Table view
<Tr t={t.tableView}/>
</button>
<button class:primary={mode === "aggregate"} on:click={() => mode = "aggregate"}>
Aggregate
<Tr t={t.aggregateView}/>
</button>
<button class:primary={mode === "images"} on:click={() => mode = "images"}>
Images
<Tr t={t.images}/>
</button>
</div>
@ -195,16 +205,16 @@
{:else if mode === "table"}
<div class="m-2 h-full overflow-y-auto">
{#each $featuresStore as f}
<History onlyShowChangesBy={$username} id={f.properties.id} />
<History onlyShowChangesBy={$username?.split(";")} id={f.properties.id} />
{/each}
</div>
{:else if mode === "aggregate"}
<div class="m-2 h-full overflow-y-auto">
<AggregateView features={$featuresStore} onlyShowUsername={$username} />
<AggregateView features={$featuresStore} onlyShowUsername={$username?.split(";")} />
</div>
{:else if mode === "images"}
<div class="m-2 h-full overflow-y-auto">
<AggregateImages features={$featuresStore} onlyShowUsername={$username} />
<AggregateImages features={$featuresStore} onlyShowUsername={$username?.split(";")} />
</div>
{/if}
</div>