2024-07-15 01:51:15 +02:00
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"
2024-08-02 19:22:28 +02:00
import Constants from "../src/Models/Constants"
2024-07-15 01:51:15 +02:00
2024-08-01 16:00:31 +02:00
type ErrorMessage = {
ip : string
index : number
date : string
message : {
stacktrace : string
message : string
layout : string
version : string
language : string
username : string
userid : number
pendingChanges : ChangeDescription [ ]
}
}
2024-07-15 01:51:15 +02:00
class HandleErrors extends Script {
constructor ( ) {
super ( "Inspects the errors made on a given day. Argument: path to errors" )
}
async main ( args : string [ ] ) : Promise < void > {
const osmConnection = new OsmConnection ( )
const downloader = new OsmObjectDownloader ( osmConnection . Backend ( ) , undefined )
const path = args [ 0 ]
const lines = readFileSync ( path , "utf8" ) . split ( "\n" )
2024-07-25 14:48:31 +02:00
const createdChangesets = new Set < string > ( )
const refusedFiles : Set < string > = new Set < string > ( )
refusedFiles . add ( "[]" )
2024-07-17 18:42:39 +02:00
2024-08-09 16:55:08 +02:00
const changesObj = new Changes (
{
dryRun : new ImmutableStore ( true ) ,
osmConnection ,
} ,
false ,
( err ) = > console . error ( err )
)
2024-07-31 11:10:35 +02:00
2024-08-01 16:00:31 +02:00
const all : ErrorMessage [ ] = [ ]
2024-07-15 01:51:15 +02:00
for ( const line of lines ) {
if ( ! line ? . trim ( ) ) {
continue
}
try {
2024-08-01 16:00:31 +02:00
const parsed : ErrorMessage = JSON . parse ( line )
2024-07-15 01:51:15 +02:00
const e = parsed . message
2024-07-17 18:42:39 +02:00
if ( e . layout === "grb" ) {
2024-07-19 11:38:01 +02:00
console . log ( "Skipping GRB " )
2024-07-15 01:51:15 +02:00
continue
}
for ( const pendingChange of e . pendingChanges ) {
2024-07-21 10:52:51 +02:00
console . log (
"\t https://osm.org/" + pendingChange . type + "/" + pendingChange . id ,
pendingChange . meta . changeType ,
2024-08-09 16:55:08 +02:00
pendingChange . doDelete ? "DELETE" : ""
2024-07-21 10:52:51 +02:00
)
2024-07-15 01:51:15 +02:00
}
2024-08-01 16:00:31 +02:00
all . push ( parsed )
2024-07-15 01:51:15 +02:00
} catch ( e ) {
console . log ( "Parsing line failed:" , e )
}
}
2024-08-01 16:00:31 +02:00
for ( const parsed of all ) {
2024-08-09 20:38:13 +02:00
console . log ( parsed . message . username , parsed . message . layout , parsed . message . message , parsed . date )
2024-08-01 16:00:31 +02:00
const e = parsed . message
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
2024-08-09 16:55:08 +02:00
const osmObjects : { id : string ; osmObj : OsmObject | "deleted" } [ ] = await Promise . all < {
id : string
osmObj : OsmObject | "deleted"
} > (
neededIds . map ( async ( id ) = > ( {
id ,
osmObj : await downloader . DownloadObjectAsync ( id ) ,
} ) )
)
2024-08-01 16:00:31 +02:00
const objects = osmObjects
. filter ( ( obj ) = > obj . osmObj !== "deleted" )
. map ( ( obj ) = > < OsmObject > obj . osmObj )
const { toUpload , refused } = changesObj . fragmentChanges ( e . pendingChanges , objects )
const changes : {
newObjects : OsmObject [ ]
modifiedObjects : OsmObject [ ]
deletedObjects : OsmObject [ ]
2024-08-09 20:38:13 +02:00
} = changesObj . CreateChangesetObjects ( toUpload , objects )
2024-08-01 16:00:31 +02:00
const changeset = Changes . createChangesetFor ( "" , changes )
const path =
"error_changeset_" + parsed . index + "_" + e . layout + "_" + e . username + ".osc"
if (
changeset ===
2024-08-02 19:22:28 +02:00
` <osmChange version='0.6' generator='Mapcomplete ${ Constants . vNumber } '></osmChange> `
2024-08-01 16:00:31 +02:00
) {
2024-08-09 20:38:13 +02:00
/ * c o n s o l e . l o g (
2024-08-09 16:55:08 +02:00
"Changes for " + parsed . index + ": empty changeset, not creating a file for it"
2024-08-09 20:38:13 +02:00
) * /
2024-08-01 16:00:31 +02:00
} else if ( createdChangesets . has ( changeset ) ) {
2024-08-09 20:38:13 +02:00
/ * c o n s o l e . l o g (
2024-08-01 16:00:31 +02:00
"Changeset " +
2024-08-09 16:55:08 +02:00
parsed . index +
" is identical to previously seen changeset, not writing to file"
2024-08-09 20:38:13 +02:00
) * /
2024-08-01 16:00:31 +02:00
} else {
2024-08-09 20:38:13 +02:00
const changesetWithMsg = ` <!-- User: ${ parsed . message . username } ( ${ parsed . message . userid } ) ${ parsed . message . layout } ; Version ${ parsed . message . version } ; Not uploaded due to ${ parsed . message . message } -->
$ { changeset } `
writeFileSync ( path , changesetWithMsg , "utf8" )
2024-08-01 16:00:31 +02:00
createdChangesets . add ( changeset )
2024-08-09 20:38:13 +02:00
console . log ( "Written" , path , "with " + e . pendingChanges . length + " changes" )
2024-08-01 16:00:31 +02:00
}
const refusedContent = JSON . stringify ( refused )
if ( refusedFiles . has ( refusedContent ) ) {
2024-08-09 20:38:13 +02:00
/ * c o n s o l e . l o g (
2024-08-01 16:00:31 +02:00
"Refused changes for " +
2024-08-09 16:55:08 +02:00
parsed . index +
" is identical to previously seen changeset, not writing to file"
2024-08-09 20:38:13 +02:00
) * /
2024-08-01 16:00:31 +02:00
} else {
writeFileSync ( path + ".refused.json" , refusedContent , "utf8" )
refusedFiles . add ( refusedContent )
2024-08-09 20:38:13 +02:00
console . log ( "Written refused" , path )
2024-08-01 16:00:31 +02:00
}
}
2024-07-15 01:51:15 +02:00
}
}
2024-08-09 16:55:08 +02:00
new HandleErrors ( ) . run ( )