From 4ef3429c5a8ce6caa14534dff3bbb84e6d60512f Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 15 Jul 2024 01:51:15 +0200 Subject: [PATCH] Improve error reporting, first attempt for script which handles the errors --- package.json | 2 +- scripts/handleErrors.ts | 100 ++++++++++++++++++++++++++++++ src/Logic/Osm/Changes.ts | 12 ++-- src/Logic/Osm/ChangesetHandler.ts | 4 +- src/Models/ThemeViewState.ts | 2 + src/Utils.ts | 8 +++ 6 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 scripts/handleErrors.ts diff --git a/package.json b/package.json index b4e0bedafb..bb3c03f748 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.44.4", + "version": "0.44.5", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues", diff --git a/scripts/handleErrors.ts b/scripts/handleErrors.ts new file mode 100644 index 0000000000..0b9e4dea66 --- /dev/null +++ b/scripts/handleErrors.ts @@ -0,0 +1,100 @@ +import Script from "./Script" +import { readFileSync, writeFileSync } from "fs" +import { ChangeDescription } from "../src/Logic/Osm/Actions/ChangeDescription" +import { Changes } from "../src/Logic/Osm/Changes" +import { OsmObject } from "../src/Logic/Osm/OsmObject" +import OsmObjectDownloader from "../src/Logic/Osm/OsmObjectDownloader" +import { OsmConnection } from "../src/Logic/Osm/OsmConnection" +import { ImmutableStore } from "../src/Logic/UIEventSource" +import { Utils } from "../src/Utils" + +class HandleErrors extends Script { + + constructor() { + super("Inspects the errors made on a given day. Argument: path to errors") + } + + async main(args: string[]): Promise { + const osmConnection = new OsmConnection() + const downloader = new OsmObjectDownloader(osmConnection.Backend(), undefined) + + const path = args[0] + const lines = readFileSync(path, "utf8").split("\n") + + for (const line of lines) { + if (!line?.trim()) { + continue + } + try { + + const parsed: { + ip: string, index: number, date: string, + message: { + stacktrace: string + message: string + layout: string + version: string + language: string + username: string + userid: number + pendingChanges: ChangeDescription[] + } + } = JSON.parse(line) + const e = parsed.message + if (e.layout !== "grb") { + continue + } + console.log(e.username, e.layout, e.message, parsed.date) + for (const pendingChange of e.pendingChanges) { + console.log("\t https://osm.org/" + pendingChange.type + "/" + pendingChange.id, pendingChange.meta.changeType, pendingChange.doDelete ? "DELETE" : "") + } + + + const neededIds = Changes.GetNeededIds(e.pendingChanges) + // We _do not_ pass in the Changes object itself - we want the data from OSM directly in order to apply the changes + let osmObjects: { id: string, osmObj: OsmObject | "deleted" }[] = await Promise.all<{ + id: string; + osmObj: OsmObject | "deleted" + }>( + neededIds.map(async id => ({ id, osmObj: await downloader.DownloadObjectAsync(id) })), + ) + + const objects = osmObjects + .filter((obj) => obj.osmObj !== "deleted") + .map((obj) => obj.osmObj) + + const perType = Array.from( + Utils.Hist( + e.pendingChanges + .filter( + (descr) => + descr.meta.changeType !== undefined && descr.meta.changeType !== null, + ) + .map((descr) => descr.meta.changeType), + ), + ([key, count]) => ({ + key: key, + value: count, + aggregate: true, + }), + ) + const changes: { + newObjects: OsmObject[] + modifiedObjects: OsmObject[] + deletedObjects: OsmObject[] + } = new Changes({ + dryRun: new ImmutableStore(true), + osmConnection, + }).CreateChangesetObjects(e.pendingChanges, objects) + + const changeset = Changes.createChangesetFor("" + parsed.index, changes) + writeFileSync("error_changeset_"+parsed.index+".osc", changeset, "utf8") + } catch (e) { + console.log("Parsing line failed:", e) + } + } + + } +} + +new HandleErrors().run() diff --git a/src/Logic/Osm/Changes.ts b/src/Logic/Osm/Changes.ts index f53a9c2391..804f35b76d 100644 --- a/src/Logic/Osm/Changes.ts +++ b/src/Logic/Osm/Changes.ts @@ -13,10 +13,6 @@ import { ChangesetHandler, ChangesetTag } from "./ChangesetHandler" import { OsmConnection } from "./OsmConnection" import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore" import OsmObjectDownloader from "./OsmObjectDownloader" -import Combine from "../../UI/Base/Combine" -import BaseUIElement from "../../UI/BaseUIElement" -import Title from "../../UI/Base/Title" -import Table from "../../UI/Base/Table" import ChangeLocationAction from "./Actions/ChangeLocationAction" import ChangeTagAction from "./Actions/ChangeTagAction" import FeatureSwitchState from "../State/FeatureSwitchState" @@ -44,7 +40,7 @@ export class Changes { private _nextId: number = -1 // Newly assigned ID's are negative private readonly previouslyCreated: OsmObject[] = [] private readonly _leftRightSensitive: boolean - private readonly _changesetHandler: ChangesetHandler + public readonly _changesetHandler: ChangesetHandler private readonly _reportError?: (string: string | Error) => void constructor( @@ -209,7 +205,7 @@ export class Changes { ].join("\n\n") } - private static GetNeededIds(changes: ChangeDescription[]) { + public static GetNeededIds(changes: ChangeDescription[]) { return Utils.Dedup(changes.filter((c) => c.id >= 0).map((c) => c.type + "/" + c.id)) } @@ -520,7 +516,7 @@ export class Changes { const osmObj = await downloader.DownloadObjectAsync(id, 0) return { id, osmObj } } catch (e) { - const msg = "Could not download OSM-object" + + const msg = "Could not download OSM-object " + id + " trying again before dropping it from the changes (" + e + @@ -530,7 +526,7 @@ export class Changes { return { id, osmObj } } } catch (e) { - const msg = "Could not download OSM-object" + + const msg = "Could not download OSM-object " + id + " dropping it from the changes (" + e + diff --git a/src/Logic/Osm/ChangesetHandler.ts b/src/Logic/Osm/ChangesetHandler.ts index f11f9c0a5f..f66a4d0fa4 100644 --- a/src/Logic/Osm/ChangesetHandler.ts +++ b/src/Logic/Osm/ChangesetHandler.ts @@ -25,7 +25,7 @@ export class ChangesetHandler { * Contains previously rewritten IDs * @private */ - private readonly _remappings = new Map() + public readonly _remappings = new Map() private readonly _reportError: (e: string | Error) => void constructor( @@ -187,7 +187,7 @@ export class ChangesetHandler { await this.UpdateTags(csId, rewrittenTags) } catch (e) { if (this._reportError) { - this._reportError("Could not reuse changeset, might be closed: " + e.stacktrace ?? "" + e) + this._reportError("Could not reuse changeset "+csId+", might be closed: " + (e.stacktrace ?? ("" + e))) } console.warn("Could not upload, changeset is probably closed: ", e) openChangeset.setData(undefined) diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 156f59b7b4..2dcbfd23b8 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -900,6 +900,8 @@ export default class ThemeViewState implements SpecialVisualizationState { username: this.osmConnection.userDetails.data?.name, userid: this.osmConnection.userDetails.data?.uid, pendingChanges: this.changes.pendingChanges.data, + previousChanges: this.changes.allChanges.data, + changeRewrites: Utils.MapToObj(this.changes._changesetHandler._remappings) }), }) } diff --git a/src/Utils.ts b/src/Utils.ts index d87b7a2bd9..7d064ebd38 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1267,13 +1267,21 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be return track[str2.length][str1.length] } + public static MapToObj( + d: Map + ): Record; public static MapToObj( d: Map, onValue: (t: V, key: string) => T + ): Record + public static MapToObj( + d: Map, + onValue: (t: V, key: string) => T = undefined ): Record { const o = {} const keys = Array.from(d.keys()) keys.sort() + onValue ??= (v => v) for (const key of keys) { o[key] = onValue(d.get(key), key) }