MapComplete/src/UI/History/AggregateView.svelte

141 lines
4.2 KiB
Svelte

<script lang="ts">
import type { Feature } from "geojson"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import OsmObjectDownloader from "../../Logic/Osm/OsmObjectDownloader"
import { OsmObject } from "../../Logic/Osm/OsmObject"
import Loading from "../Base/Loading.svelte"
import { HistoryUtils } from "./HistoryUtils"
import * as shared_questions from "../../assets/generated/layers/questions.json"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import Tr from "../Base/Tr.svelte"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
import Translations from "../i18n/Translations"
import TagRenderingChart from "../BigComponents/TagRenderingChart"
import ToSvelte from "../Base/ToSvelte.svelte"
import type { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson"
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)))
)
let allDiffs: Store<
{
key: string
value?: string
oldValue?: string
step: OsmObject
}[]
> = allHistories.mapD((histories) => HistoryUtils.fullHistoryDiff(histories, usernames))
const trs = shared_questions.tagRenderings.map(
(tr) => new TagRenderingConfig(<TagRenderingConfigJson>tr)
)
function detectQuestion(key: string): TagRenderingConfig {
return trs.find((tr) => tr.freeform?.key === key)
}
const mergedCount: Store<
{
key: string
tr: TagRenderingConfig
count: number
values: { value: string; count: number }[]
features: Feature[]
}[]
> = allDiffs.mapD((allDiffs) => {
const keyCounts = new Map<string, Map<string, OsmObject[]>>()
for (const diff of allDiffs) {
const k = diff.key
if (!keyCounts.has(k)) {
keyCounts.set(k, new Map<string, OsmObject[]>())
}
const valueCounts = keyCounts.get(k)
const v = diff.value ?? ""
const oldFeaturesList = valueCounts.get(v) ?? []
valueCounts.set(v, [...oldFeaturesList, diff.step])
}
const perKey: {
key: string
tr: TagRenderingConfig
count: number
values: { value: string; count: number }[]
features: Feature[]
}[] = []
keyCounts.forEach((values: Map<string, OsmObject[]>, key: string) => {
const keyTotal: { value: string; features: Feature[] }[] = []
values.forEach((count, value) => {
keyTotal.push({ value, features: count.map((step) => step.asGeoJson()) })
})
let countForKey: Feature[] = []
for (const { features } of keyTotal) {
countForKey.push(...features)
}
keyTotal.sort((a, b) => b.features.length - a.features.length)
const tr = detectQuestion(key)
perKey.push({
count: countForKey.length,
tr,
key,
values: keyTotal.map(({ value, features }) => ({
value,
count: features.length,
})),
features: countForKey,
})
})
perKey.sort((a, b) => b.count - a.count)
return perKey
})
const t = Translations.t.inspector
</script>
{#if allHistories === undefined}
<Loading />
{:else if $allDiffs !== undefined}
{#each $mergedCount as diff}
<h3>
{#if diff.tr}
<Tr t={diff.tr.question} />
{:else}
{diff.key}
{/if}
</h3>
<AccordionSingle>
<span slot="header">
<Tr t={t.answeredCountTimes.Subs(diff)} />
</span>
<ul>
{#each diff.values as value}
<li>
<b>{value.value}</b>
{#if value.count > 1}
- {value.count}
{/if}
</li>
{/each}
</ul>
{#if diff.tr}
<div class="h-48 w-48">
<ToSvelte
construct={new TagRenderingChart(diff.features, diff.tr, {
groupToOtherCutoff: 0,
chartType: "pie",
sort: true,
})}
/>
</div>
{:else}
Could not create a graph
{/if}
</AccordionSingle>
{/each}
{/if}