2024-07-15 01:51:15 +02:00
import Script from "./Script"
2024-08-24 01:50:55 +02:00
import { appendFileSync , readFileSync , writeFile , writeFileSync } from "fs"
2024-07-15 01:51:15 +02:00
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" )
}
2024-08-24 01:50:55 +02:00
private readonly ignoreUsers = new Set < string > ( [ ] )
2024-09-02 12:48:15 +02:00
private async handleError (
parsed : ErrorMessage ,
changesObj : Changes ,
downloader : OsmObjectDownloader ,
createdChangesets : Set < string > ,
refusedFiles : Set < string >
) {
2024-08-24 01:50:55 +02:00
console . log (
parsed . message . username ,
parsed . message . layout ,
parsed . message . message ,
2024-09-02 12:48:15 +02:00
parsed . date
2024-08-24 01:50:55 +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
const osmObjects : { id : string ; osmObj : OsmObject | "deleted" } [ ] = await Promise . all < {
id : string
osmObj : OsmObject | "deleted"
} > (
neededIds . map ( async ( id ) = > {
try {
const osmObj = await downloader . DownloadObjectAsync ( id )
2024-09-02 12:48:15 +02:00
return {
2024-08-24 01:50:55 +02:00
id ,
osmObj ,
2024-09-02 12:48:15 +02:00
}
2024-08-24 01:50:55 +02:00
} catch ( e ) {
console . error ( "COULD NOT DOWNLOAD OBJECT" , id )
return {
id ,
osmObj : "deleted" ,
}
}
2024-09-02 12:48:15 +02:00
} )
2024-08-24 01:50:55 +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 [ ]
} = changesObj . CreateChangesetObjects ( toUpload , objects , true )
2024-08-24 12:18:48 +02:00
const changeset = Changes . buildChangesetXML ( "" , changes )
2024-09-02 12:48:15 +02:00
const path = "error_changeset_" + parsed . index + "_" + e . layout + "_" + e . username + ".osc"
2024-08-24 01:50:55 +02:00
if (
changeset ===
` <osmChange version='0.6' generator='Mapcomplete ${ Constants . vNumber } '></osmChange> `
) {
/ * c o n s o l e . l o g (
"Changes for " + parsed . index + ": empty changeset, not creating a file for it"
) * /
} else if ( createdChangesets . has ( changeset ) ) {
/ * c o n s o l e . l o g (
"Changeset " +
parsed . index +
" is identical to previously seen changeset, not writing to file"
) * /
} else {
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" )
createdChangesets . add ( changeset )
console . log ( "Written" , path , "with " + e . pendingChanges . length + " changes" )
}
const refusedContent = JSON . stringify ( refused )
if ( refusedFiles . has ( refusedContent ) ) {
/ * c o n s o l e . l o g (
"Refused changes for " +
parsed . index +
" is identical to previously seen changeset, not writing to file"
) * /
} else {
writeFileSync ( path + ".refused.json" , refusedContent , "utf8" )
refusedFiles . add ( refusedContent )
console . log ( "Written refused" , path )
}
}
2024-07-15 01:51:15 +02:00
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 ,
2024-09-02 12:48:15 +02:00
( err ) = > console . error ( err )
2024-08-09 16:55:08 +02:00
)
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
}
2024-08-24 01:50:55 +02:00
if ( this . ignoreUsers . has ( e . username ) ) {
continue
}
2024-07-15 01:51:15 +02:00
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-09-02 12:48:15 +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-24 01:50:55 +02:00
try {
2024-09-02 12:48:15 +02:00
await this . handleError (
parsed ,
changesObj ,
downloader ,
createdChangesets ,
refusedFiles
)
2024-08-24 01:50:55 +02:00
} catch ( e ) {
console . error ( "ERROR: could not handle " , parsed , " due to" , e )
2024-09-02 12:48:15 +02:00
writeFileSync (
"ERRORS." + parsed . index ,
"ERROR: due to " + e + ": could not handle\n" + JSON . stringify ( parsed ) ,
"utf8"
)
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 ( )