Browse Source

Get the code working again!!!!!!

master
t123yh 7 years ago
parent
commit
676cac63fe
  1. 3
      .vscode/settings.json
  2. 4
      daemon-config-example.json
  3. 425
      package-lock.json
  4. 5
      package.json
  5. 4
      runner-shared-config-example.json
  6. 13
      src/daemon-frontend-syzoj/config.ts
  7. 2
      src/daemon-frontend-syzoj/daemonRouter.ts
  8. 59
      src/daemon-frontend-syzoj/index.ts
  9. 40
      src/daemon-frontend-syzoj/rmq.ts
  10. 190
      src/daemon-frontend-syzoj/socketio.ts
  11. 14
      src/daemon/config.ts
  12. 8
      src/daemon/index.ts
  13. 6
      src/daemon/interfaces.ts
  14. 12
      src/daemon/judge/compile.ts
  15. 62
      src/daemon/judge/index.ts
  16. 121
      src/daemon/judge/judger-base.ts
  17. 17
      src/daemon/judge/process.ts
  18. 144
      src/daemon/judge/standard.ts
  19. 22
      src/daemon/rmq.ts
  20. 2
      src/daemon/testData.ts
  21. 43
      src/interfaces.ts
  22. 50
      src/judgeResult.ts
  23. 29
      src/rmq-common.ts
  24. 14
      src/runner/compile.ts
  25. 13
      src/runner/config.ts
  26. 3
      src/runner/executable.ts
  27. 32
      src/runner/judge.ts
  28. 20
      src/winston-common.ts

3
.vscode/settings.json vendored

@ -0,0 +1,3 @@
//
{
}

4
daemon-config-example.json

@ -1,7 +1,7 @@
{ {
"RabbitMQUrl": "amqp://localhost/", "RabbitMQUrl": "amqp://localhost/",
"RedisUrl": "redis://127.0.0.1:6379", "RedisUrl": "redis://127.0.0.1:6379",
"TestData": "/home/t123yh/syzoj/uploads/testdata", "TestData": "/home/t123yh/sync2/syzoj/uploads/testdata",
"Priority": 1, "Priority": 1,
"DataDisplayLimit": 100 "DataDisplayLimit": 100
} }

425
package-lock.json generated

@ -79,6 +79,15 @@
"integrity": "sha512-bNVEiBrpEdg5oWz/10gxLUSjQGeBx7HgJHgy2DfR1K7unn260FfRwjVYH/NngQucYeMqsg1hOSo0+heQLJAwZA==", "integrity": "sha512-bNVEiBrpEdg5oWz/10gxLUSjQGeBx7HgJHgy2DfR1K7unn260FfRwjVYH/NngQucYeMqsg1hOSo0+heQLJAwZA==",
"dev": true "dev": true
}, },
"@types/jsonwebtoken": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.3.tgz",
"integrity": "sha512-cVhxZfVCyTZd1P+2a+xXSR9to7hZTulNRLLCQMVfAevUqx2Ee+EgsiD/7pX8qvdXWP3nWgSoTjKRLMrIpdPVjQ==",
"dev": true,
"requires": {
"@types/node": "7.0.39"
}
},
"@types/klaw": { "@types/klaw": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/klaw/-/klaw-1.3.2.tgz", "resolved": "https://registry.npmjs.org/@types/klaw/-/klaw-1.3.2.tgz",
@ -170,6 +179,15 @@
"@types/mime": "1.3.1" "@types/mime": "1.3.1"
} }
}, },
"@types/socket.io": {
"version": "1.4.29",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-1.4.29.tgz",
"integrity": "sha1-hqazqat4z5qQDO74W5totr6oZxI=",
"dev": true,
"requires": {
"@types/node": "7.0.39"
}
},
"@types/uuid": { "@types/uuid": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.0.tgz", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.0.tgz",
@ -197,6 +215,11 @@
"negotiator": "0.6.1" "negotiator": "0.6.1"
} }
}, },
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"ajv": { "ajv": {
"version": "4.11.8", "version": "4.11.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
@ -217,6 +240,16 @@
"readable-stream": "1.1.14" "readable-stream": "1.1.14"
} }
}, },
"ansi-regex": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
"integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk="
},
"ansi-styles": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
"integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94="
},
"argparse": { "argparse": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
@ -243,6 +276,11 @@
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz",
"integrity": "sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0=" "integrity": "sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0="
}, },
"arraybuffer.slice": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz",
"integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco="
},
"asn1": { "asn1": {
"version": "0.2.3", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
@ -273,6 +311,26 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
}, },
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64id": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
"integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY="
},
"base64url": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz",
"integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs="
},
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
@ -282,6 +340,14 @@
"tweetnacl": "0.14.5" "tweetnacl": "0.14.5"
} }
}, },
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"bindings": { "bindings": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
@ -295,6 +361,11 @@
"buffer-more-ints": "0.0.2" "buffer-more-ints": "0.0.2"
} }
}, },
"blob": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
"integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE="
},
"bluebird": { "bluebird": {
"version": "3.5.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
@ -325,6 +396,11 @@
"hoek": "2.16.3" "hoek": "2.16.3"
} }
}, },
"buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"buffer-more-ints": { "buffer-more-ints": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz", "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz",
@ -335,11 +411,28 @@
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
"integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=" "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk="
}, },
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"caseless": { "caseless": {
"version": "0.12.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
}, },
"chalk": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
"integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
"requires": {
"ansi-styles": "1.1.0",
"escape-string-regexp": "1.0.5",
"has-ansi": "0.1.0",
"strip-ansi": "0.3.0",
"supports-color": "0.2.0"
}
},
"co": { "co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -368,6 +461,21 @@
"typical": "2.6.1" "typical": "2.6.1"
} }
}, },
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"content-disposition": { "content-disposition": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
@ -463,6 +571,15 @@
"jsbn": "0.1.1" "jsbn": "0.1.1"
} }
}, },
"ecdsa-sig-formatter": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz",
"integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=",
"requires": {
"base64url": "2.0.0",
"safe-buffer": "5.1.1"
}
},
"ee-first": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -473,11 +590,61 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
"integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
}, },
"engine.io": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.0.tgz",
"integrity": "sha1-XKQ4486f28kVxKIcjdnhJmcG5X4=",
"requires": {
"accepts": "1.3.3",
"base64id": "1.0.0",
"cookie": "0.3.1",
"debug": "2.6.7",
"engine.io-parser": "2.1.1",
"uws": "0.14.5",
"ws": "2.3.1"
}
},
"engine.io-client": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.1.tgz",
"integrity": "sha1-QVqYUrrbFPoAj6PvHjFgjbZ2EyU=",
"requires": {
"component-emitter": "1.2.1",
"component-inherit": "0.0.3",
"debug": "2.6.7",
"engine.io-parser": "2.1.1",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parsejson": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"ws": "2.3.1",
"xmlhttprequest-ssl": "1.5.3",
"yeast": "0.1.2"
}
},
"engine.io-parser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.1.tgz",
"integrity": "sha1-4Ps/DgRi9/WLt3waUun1p+JuRmg=",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "0.0.6",
"base64-arraybuffer": "0.1.5",
"blob": "0.0.4",
"has-binary2": "1.0.2"
}
},
"escape-html": { "escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
}, },
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"esprima": { "esprima": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
@ -651,6 +818,34 @@
"har-schema": "1.0.5" "har-schema": "1.0.5"
} }
}, },
"has-ansi": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
"integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
"requires": {
"ansi-regex": "0.2.1"
}
},
"has-binary2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz",
"integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=",
"requires": {
"isarray": "2.0.1"
},
"dependencies": {
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
}
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"hawk": { "hawk": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
@ -705,6 +900,11 @@
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
}, },
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
@ -730,11 +930,27 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
}, },
"isemail": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz",
"integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo="
},
"isstream": { "isstream": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
}, },
"joi": {
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz",
"integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=",
"requires": {
"hoek": "2.16.3",
"isemail": "1.2.0",
"moment": "2.18.1",
"topo": "1.1.0"
}
},
"js-yaml": { "js-yaml": {
"version": "3.9.1", "version": "3.9.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz",
@ -768,6 +984,14 @@
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
}, },
"jsondiffpatch": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.2.4.tgz",
"integrity": "sha1-1LbFOz/H2htLkcHCrsi5MrdRHVw=",
"requires": {
"chalk": "0.5.1"
}
},
"jsonfile": { "jsonfile": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz",
@ -781,6 +1005,18 @@
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
}, },
"jsonwebtoken": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.2.tgz",
"integrity": "sha1-VxuQPAfodcD8WSA9GseGZ9gOCc0=",
"requires": {
"joi": "6.10.1",
"jws": "3.1.4",
"lodash.once": "4.1.1",
"ms": "2.0.0",
"xtend": "4.0.1"
}
},
"jsprim": { "jsprim": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@ -799,6 +1035,27 @@
} }
} }
}, },
"jwa": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz",
"integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=",
"requires": {
"base64url": "2.0.0",
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.9",
"safe-buffer": "5.1.1"
}
},
"jws": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz",
"integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=",
"requires": {
"base64url": "2.0.0",
"jwa": "1.1.5",
"safe-buffer": "5.1.1"
}
},
"klaw": { "klaw": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz",
@ -817,6 +1074,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
}, },
"lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
},
"media-typer": { "media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -886,6 +1148,11 @@
} }
} }
}, },
"moment": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
"integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8="
},
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -917,6 +1184,16 @@
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
}, },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-component": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
},
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@ -925,6 +1202,30 @@
"ee-first": "1.1.1" "ee-first": "1.1.1"
} }
}, },
"parsejson": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz",
"integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=",
"requires": {
"better-assert": "1.0.2"
}
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
"requires": {
"better-assert": "1.0.2"
}
},
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "1.0.2"
}
},
"parseurl": { "parseurl": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
@ -1150,6 +1451,62 @@
"hoek": "2.16.3" "hoek": "2.16.3"
} }
}, },
"socket.io": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.3.tgz",
"integrity": "sha1-Q1nwaiSTOua9CHeYr3jGgOrjReM=",
"requires": {
"debug": "2.6.7",
"engine.io": "3.1.0",
"object-assign": "4.1.1",
"socket.io-adapter": "1.1.1",
"socket.io-client": "2.0.3",
"socket.io-parser": "3.1.2"
}
},
"socket.io-adapter": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
"integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
},
"socket.io-client": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.3.tgz",
"integrity": "sha1-bK9K/5+FsZ/ZG2zhPWmttWT4hzs=",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "2.6.7",
"engine.io-client": "3.1.1",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "3.1.2",
"to-array": "0.1.4"
}
},
"socket.io-parser": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz",
"integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=",
"requires": {
"component-emitter": "1.2.1",
"debug": "2.6.7",
"has-binary2": "1.0.2",
"isarray": "2.0.1"
},
"dependencies": {
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
}
}
},
"source-map": { "source-map": {
"version": "0.5.6", "version": "0.5.6",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
@ -1215,6 +1572,19 @@
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
}, },
"strip-ansi": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
"integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
"requires": {
"ansi-regex": "0.2.1"
}
},
"supports-color": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
"integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo="
},
"tar": { "tar": {
"version": "3.1.9", "version": "3.1.9",
"resolved": "https://registry.npmjs.org/tar/-/tar-3.1.9.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-3.1.9.tgz",
@ -1235,6 +1605,19 @@
"typical": "2.6.1" "typical": "2.6.1"
} }
}, },
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"topo": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz",
"integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=",
"requires": {
"hoek": "2.16.3"
}
},
"tough-cookie": { "tough-cookie": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
@ -1277,6 +1660,11 @@
"resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
"integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
}, },
"ultron": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz",
"integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ="
},
"universalify": { "universalify": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
@ -1297,6 +1685,12 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
}, },
"uws": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz",
"integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=",
"optional": true
},
"vary": { "vary": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz",
@ -1332,10 +1726,41 @@
"stack-trace": "0.0.10" "stack-trace": "0.0.10"
} }
}, },
"ws": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
"integrity": "sha1-a5Sz5EfLajY/eF6vlK9jWejoHIA=",
"requires": {
"safe-buffer": "5.0.1",
"ultron": "1.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
"integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c="
}
}
},
"xmlhttprequest-ssl": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz",
"integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0="
},
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
},
"yallist": { "yallist": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
} }
} }
} }

5
package.json

@ -15,6 +15,8 @@
"fs-extra": "^3.0.1", "fs-extra": "^3.0.1",
"get-folder-size": "^1.0.0", "get-folder-size": "^1.0.0",
"js-yaml": "^3.9.1", "js-yaml": "^3.9.1",
"jsondiffpatch": "^0.2.4",
"jsonwebtoken": "^7.4.2",
"klaw": "^2.0.0", "klaw": "^2.0.0",
"lockfile": "^1.0.3", "lockfile": "^1.0.3",
"lodash": "^4.17.4", "lodash": "^4.17.4",
@ -26,6 +28,7 @@
"request": "^2.81.0", "request": "^2.81.0",
"request-promise": "^4.2.1", "request-promise": "^4.2.1",
"simple-sandbox": "^0.3.3", "simple-sandbox": "^0.3.3",
"socket.io": "^2.0.3",
"source-map-support": "^0.4.15", "source-map-support": "^0.4.15",
"tar": "^3.1.9", "tar": "^3.1.9",
"uuid": "^3.1.0", "uuid": "^3.1.0",
@ -39,6 +42,7 @@
"@types/express": "^4.0.36", "@types/express": "^4.0.36",
"@types/fs-extra": "^3.0.3", "@types/fs-extra": "^3.0.3",
"@types/js-yaml": "^3.9.0", "@types/js-yaml": "^3.9.0",
"@types/jsonwebtoken": "^7.2.3",
"@types/klaw": "^1.3.2", "@types/klaw": "^1.3.2",
"@types/lodash": "^4.14.71", "@types/lodash": "^4.14.71",
"@types/msgpack-lite": "^0.1.4", "@types/msgpack-lite": "^0.1.4",
@ -47,6 +51,7 @@
"@types/redlock": "0.0.31", "@types/redlock": "0.0.31",
"@types/request": "^2.0.0", "@types/request": "^2.0.0",
"@types/request-promise": "^4.1.36", "@types/request-promise": "^4.1.36",
"@types/socket.io": "^1.4.29",
"@types/uuid": "^3.4.0", "@types/uuid": "^3.4.0",
"@types/winston": "^2.3.4", "@types/winston": "^2.3.4",
"typescript": "^2.3.4" "typescript": "^2.3.4"

4
runner-shared-config-example.json

@ -1,7 +1,7 @@
{ {
"RabbitMQUrl": "amqp://localhost/", "RabbitMQUrl": "amqp://localhost/",
"RedisUrl": "redis://127.0.0.1:6379", "RedisUrl": "redis://127.0.0.1:6379",
"TestData": "/home/t123yh/syzoj/uploads/testdata", "TestData": "/home/t123yh/sync2/syzoj/uploads/testdata",
"Priority": 1, "Priority": 1,
"DebugMessageDisplayLimit": 5000, "DebugMessageDisplayLimit": 5000,
"OutputLimit": 104857600, "OutputLimit": 104857600,
@ -17,4 +17,4 @@
"SandboxUser": "nobody", "SandboxUser": "nobody",
"SandboxRoot": "/home/t123yh/alpine", "SandboxRoot": "/home/t123yh/alpine",
"BinaryDirectory": "/home/t123yh/syzoj-bin" "BinaryDirectory": "/home/t123yh/syzoj-bin"
} }

13
src/daemon-frontend-syzoj/config.ts

@ -1,6 +1,7 @@
import * as commandLineArgs from 'command-line-args'; import commandLineArgs = require('command-line-args');
import * as fs from 'fs'; import fs = require('fs');
import * as winston from 'winston'; import winston = require('winston');
import { configureWinston } from '../winston-common';
export interface ConfigStructure { export interface ConfigStructure {
rabbitMQ: string; rabbitMQ: string;
@ -28,8 +29,4 @@ export const globalConfig: ConfigStructure = {
token: configJSON.Token token: configJSON.Token
} }
if (options.verbose) { configureWinston(options.verbose);
(winston as any).level = 'debug';
} else {
(winston as any).level = 'warn';
}

2
src/daemon-frontend-syzoj/taskRouter.ts → src/daemon-frontend-syzoj/daemonRouter.ts

@ -1,4 +1,5 @@
import express = require('express'); import express = require('express');
import winston = require('winston');
import { globalConfig as Cfg } from './config'; import { globalConfig as Cfg } from './config';
import { pushTask } from './rmq'; import { pushTask } from './rmq';
@ -18,6 +19,7 @@ taskRouter.put('/task', (req, res) => {
return res.sendStatus(400); return res.sendStatus(400);
} }
try { try {
winston.info("Got task: " + req.body);
pushTask(req.body); pushTask(req.body);
return res.status(200).send('OK'); return res.status(200).send('OK');
} catch (err) { } catch (err) {

59
src/daemon-frontend-syzoj/index.ts

@ -3,32 +3,65 @@ require('source-map-support').install();
import express = require('express'); import express = require('express');
import bodyParser = require('body-parser'); import bodyParser = require('body-parser');
import Bluebird = require('bluebird'); import Bluebird = require('bluebird');
import url = require('url'); import urlLib = require('url');
import rp = require('request-promise'); import rp = require('request-promise');
import winston = require('winston'); import winston = require('winston');
import http = require('http');
import { globalConfig as Cfg } from './config'; import { globalConfig as Cfg } from './config';
import { connect, waitForResult } from './rmq'; import { connect, waitForResult, waitForProgress } from './rmq';
import { convertResult } from '../judgeResult'; import { convertResult } from '../judgeResult';
import taskRouter from './taskRouter'; import { ProgressReportType, OverallResult, TaskStatus, CompilationResult } from '../interfaces';
import taskRouter from './daemonRouter';
import { initializeSocketIO, createTask, updateCompileStatus, updateProgress, updateResult } from './socketio';
const app = express(); const app = express();
app.use(bodyParser.json()); app.use(bodyParser.json());
app.use('/daemon', taskRouter); app.use('/daemon', taskRouter);
(async () => { (async () => {
await connect(); await connect();
await waitForResult(async (result) => { await waitForResult(async (result) => {
await rp(url.resolve(Cfg.remoteUrl, "api/v2/judge/update2"), { winston.info("Reporting...", result);
method: 'POST',
body: convertResult(result.taskId, result.progress), const submit = async function (url, obj) {
headers: { winston.debug(`POST ${Cfg.remoteUrl}, data = ${JSON.stringify(obj)}`);
Token: Cfg.token await rp(urlLib.resolve(Cfg.remoteUrl, url), {
}, method: 'POST',
json: true, body: obj,
simple: true headers: {
}); Token: Cfg.token
},
json: true,
simple: true
});
}
if (result.type === ProgressReportType.Finished) {
await submit("api/v2/judge/finished", convertResult(result.taskId, result.progress as OverallResult));
} else if (result.type === ProgressReportType.Compiled) {
await submit("api/v2/judge/compiled", {
taskId: result.taskId,
result: result.progress
});
} else {
}
winston.verbose("Reported.");
});
await waitForProgress(async (result) => {
if (result.type === ProgressReportType.Started) {
createTask(result.taskId);
} else if (result.type === ProgressReportType.Compiled) {
updateCompileStatus(result.taskId, result.progress as CompilationResult);
} else if (result.type === ProgressReportType.Progress) {
updateProgress(result.taskId, result.progress as OverallResult);
} else if (result.type === ProgressReportType.Finished) {
updateResult(result.taskId, result.progress as OverallResult);
}
}); });
})().then(() => { })().then(() => {
app.listen(Cfg.listen.port, Cfg.listen.host); const server = http.createServer(app);
server.listen(Cfg.listen.port, Cfg.listen.host);
}); });

40
src/daemon-frontend-syzoj/rmq.ts

@ -5,6 +5,7 @@ import winston = require('winston');
import util = require('util'); import util = require('util');
import { cleanUp } from './cleanup'; import { cleanUp } from './cleanup';
import * as rmqCommon from '../rmq-common'; import * as rmqCommon from '../rmq-common';
import requestErrors = require('request-promise/errors');
import { JudgeResult, ProgressReportData } from '../interfaces'; import { JudgeResult, ProgressReportData } from '../interfaces';
let amqpConnection: amqp.Connection; let amqpConnection: amqp.Connection;
@ -17,6 +18,7 @@ export async function connect() {
publicChannel = await newChannel(); publicChannel = await newChannel();
await rmqCommon.assertJudgeQueue(publicChannel); await rmqCommon.assertJudgeQueue(publicChannel);
await rmqCommon.assertResultReportQueue(publicChannel); await rmqCommon.assertResultReportQueue(publicChannel);
await rmqCommon.assertProgressReportExchange(publicChannel);
amqpConnection.on('error', (err) => { amqpConnection.on('error', (err) => {
winston.error(`RabbitMQ connection failure: ${err.toString()}`); winston.error(`RabbitMQ connection failure: ${err.toString()}`);
cleanUp(2); cleanUp(2);
@ -38,35 +40,25 @@ export function pushTask(task: any) {
} }
export async function waitForResult(handle: (result: ProgressReportData) => Promise<void>) { export async function waitForResult(handle: (result: ProgressReportData) => Promise<void>) {
const channel = await newChannel(); await rmqCommon.waitForTask(amqpConnection, rmqCommon.resultReportQueueName, null, (err) => {
channel.prefetch(1); if (err instanceof requestErrors.RequestError || err instanceof requestErrors.StatusCodeError || err instanceof requestErrors.TransformError) {
await channel.consume(rmqCommon.resultReportQueueName, (msg: amqp.Message) => { return true;
winston.info(`Got result from queue`); } else return false;
(async () => { }, handle);
const data = msgpack.decode(msg.content);
await handle(data);
})().then(async () => {
channel.ack(msg);
}, async (err) => {
winston.warn(`Failed to process message ${err.toString()}, try again`);
setTimeout(() => { channel.nack(msg, false, true) }, 500);
});
});
} }
export async function waitForProgress(handle: (result: ProgressReportData) => Promise<void>) { export async function waitForProgress(handle: (result: ProgressReportData) => Promise<void>) {
const channel = await newChannel(); const channel = await newChannel();
channel.prefetch(1); const queueName = (await channel.assertQueue('', { exclusive: true })).queue;
await channel.consume(rmqCommon.resultReportQueueName, (msg: amqp.Message) => { await channel.bindQueue(queueName, rmqCommon.progressExchangeName, '');
winston.info(`Got result from queue`); await channel.consume(queueName, (msg: amqp.Message) => {
(async () => { const data = msgpack.decode(msg.content) as ProgressReportData;
const data = msgpack.decode(msg.content); winston.verbose(`Got result from progress exchange, id: ${data.taskId}`);
await handle(data);
})().then(async () => { handle(data).then(async () => {
channel.ack(msg); channel.ack(msg)
}, async (err) => { }, async (err) => {
winston.warn(`Failed to process message ${err.toString()}, try again`); channel.nack(msg, false, false);
setTimeout(() => { channel.nack(msg, false, true) }, 500);
}); });
}); });
} }

190
src/daemon-frontend-syzoj/socketio.ts

@ -0,0 +1,190 @@
import http = require('http');
import socketio = require('socket.io');
import diff = require('jsondiffpatch');
import jwt = require('jsonwebtoken');
import { globalConfig as Cfg } from './config';
import { convertResult } from '../judgeResult';
import { JudgeResult, TaskStatus, CompilationResult, OverallResult } from '../interfaces';
interface JudgeData {
running: boolean;
current?: OverallResult;
}
interface RoughResult {
result: string;
score: number;
time: number;
memory: number;
}
let ioInstance: SocketIO.Server;
let detailProgressNamespace: SocketIO.Namespace;
// To do: find a better name
let roughProgressNamespace: SocketIO.Namespace;
// Provide support for NOI contests in which participants
// can only see whether his / her submission is successfully compiled.
let compileProgressNamespace: SocketIO.Namespace;
const currentJudgeList: JudgeData[] = [];
const finishedJudgeList = {};
export function initializeSocketIO(s: http.Server) {
ioInstance = socketio(http);
detailProgressNamespace = ioInstance.of('/detail');
roughProgressNamespace = ioInstance.of('/rough');
compileProgressNamespace = ioInstance.of('/compile');
// TODO: deduplicate the following code.
detailProgressNamespace.on('connection', (socket) => {
socket.on('join', (reqJwt, cb) => {
let req;
try {
req = jwt.verify(reqJwt, Cfg.token);
} catch (err) {
cb({
ok: false,
message: err.toString()
});
return;
}
const taskId = req.taskId;
socket.join(taskId.toString());
if (finishedJudgeList[taskId]) {
cb({
ok: true,
finished: true
});
} else {
cb({
ok: true,
finished: false,
current: currentJudgeList[taskId]
});
}
});
});
roughProgressNamespace.on('connection', (socket) => {
socket.on('join', (reqJwt, cb) => {
let req;
try {
req = jwt.verify(reqJwt, Cfg.token);
} catch (err) {
cb({
ok: false,
message: err.toString()
});
return;
}
const taskId = req.taskId;
socket.join(taskId.toString());
if (currentJudgeList[taskId]) {
cb({
ok: true,
running: true,
finished: false
});
} else if (finishedJudgeList[taskId]) {
// This is not likely to happen. If some task is processed,
// The client should not process it.
cb({
ok: true,
running: false,
finished: true
});
} else {
cb({
ok: true,
running: false,
finished: false
})
}
});
});
compileProgressNamespace.on('connection', (socket) => {
socket.on('join', (reqJwt, cb) => {
let req;
try {
req = jwt.verify(reqJwt, Cfg.token);
} catch (err) {
cb({
ok: false,
message: err.toString()
});
return;
}
const taskId = req.taskId;
socket.join(taskId.toString());
if (finishedJudgeList[taskId]) {
cb({
ok: true,
finished: true
});
} else if (currentJudgeList[taskId]
&& currentJudgeList[taskId].current
&& currentJudgeList[taskId].current.compile) {
cb({
ok: true,
finished: true
});
} else {
cb({
ok: true,
finished: false
});
}
});
});
}
export function createTask(taskId: number) {
detailProgressNamespace.to(taskId.toString()).emit("start");
roughProgressNamespace.to(taskId.toString()).emit("start");
compileProgressNamespace.to(taskId.toString()).emit("start");
currentJudgeList[taskId] = { running: true, current: null };
}
export function updateCompileStatus(taskId: number, result: CompilationResult) {
compileProgressNamespace.to(taskId.toString()).emit('compiled', {
taskId: taskId,
result: {
ok: result.status === TaskStatus.Done,
message: result.message
}
});
}
export function updateProgress(taskId: number, data: OverallResult) {
// currentJudgeList[taskId].current = data;
const original = currentJudgeList[taskId].current;
const delta = diff.diff(original, data);
detailProgressNamespace.to(taskId.toString()).emit('update', {
taskId: taskId,
delta: delta
});
currentJudgeList[taskId].current = data;
}
export function updateResult(taskId: number, data: OverallResult) {
const finalResult = convertResult(taskId, data);
const roughResult = {
result: finalResult.statusString,
time: finalResult.time,
memory: finalResult.memory,
score: finalResult.score
};
roughProgressNamespace.to(taskId.toString()).emit('finish', {
taskId: taskId,
result: roughResult
});
detailProgressNamespace.to(taskId.toString()).emit('finish', {
taskId: taskId,
result: data
});
delete currentJudgeList[taskId];
finishedJudgeList[taskId] = true;
}

14
src/daemon/config.ts

@ -1,6 +1,7 @@
import * as commandLineArgs from 'command-line-args'; import commandLineArgs = require('command-line-args');
import * as fs from 'fs'; import fs = require('fs');
import * as winston from 'winston'; import winston = require('winston');
import { configureWinston } from '../winston-common';
export interface ConfigStructure { export interface ConfigStructure {
rabbitMQ: string; rabbitMQ: string;
@ -30,9 +31,4 @@ export const globalConfig: ConfigStructure = {
dataDisplayLimit: configJSON.DataDisplayLimit dataDisplayLimit: configJSON.DataDisplayLimit
} }
if (options.verbose) { configureWinston(options.verbose);
// winston.transports.Console.level = 'debug';
(winston as any).level = 'debug';
} else {
(winston as any).level = 'warn';
}

8
src/daemon/index.ts

@ -5,18 +5,22 @@ import { globalConfig as Cfg } from './config';
import util = require('util'); import util = require('util');
import rmq = require('./rmq'); import rmq = require('./rmq');
import { judge } from './judge'; import { judge } from './judge';
import { JudgeResult, ErrorType, ProgressReportType } from '../interfaces'; import { JudgeResult, ErrorType, ProgressReportType, OverallResult } from '../interfaces';
(async function () { (async function () {
winston.info("Daemon starts."); winston.info("Daemon starts.");
await rmq.connect(); await rmq.connect();
winston.info("Start consuming the queue."); winston.info("Start consuming the queue.");
await rmq.waitForTask(async (task) => { await rmq.waitForTask(async (task) => {
let result: JudgeResult; let result: OverallResult;
try { try {
await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Started, progress: null }); await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Started, progress: null });
result = await judge(task, async (progress) => { result = await judge(task, async (progress) => {
await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Progress, progress: progress }); await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Progress, progress: progress });
}, async (progress) => {
const data = { taskId: task.taskId, type: ProgressReportType.Compiled, progress: progress };
await rmq.reportProgress(data);
await rmq.reportResult(data);
}); });
} catch (err) { } catch (err) {
winston.warn(`Judge error!!! TaskId: ${task.taskId}`, err); winston.warn(`Judge error!!! TaskId: ${task.taskId}`, err);

6
src/daemon/interfaces.ts

@ -1,5 +1,5 @@
import { Language } from '../languages'; import { Language } from '../languages';
import { FileContent, TaskStatus, TaskResult } from '../interfaces'; import { FileContent, TaskStatus, TestcaseResult } from '../interfaces';
export enum ProblemType { export enum ProblemType {
Standard = 1, Standard = 1,
@ -41,7 +41,7 @@ export enum SubtaskScoringType {
Multiple Multiple
} }
export interface TestCaseJudge { export interface TestcaseJudge {
input?: string; input?: string;
output?: string; output?: string;
userOutputFile?: string; userOutputFile?: string;
@ -51,7 +51,7 @@ export interface TestCaseJudge {
export interface SubtaskJudge { export interface SubtaskJudge {
type: SubtaskScoringType; type: SubtaskScoringType;
score: number; score: number;
cases: TestCaseJudge[]; cases: TestcaseJudge[];
} }
export interface Executable { export interface Executable {

12
src/daemon/judge/compile.ts

@ -2,17 +2,21 @@ import { Language } from '../../languages';
import * as redis from '../redis'; import * as redis from '../redis';
import * as rmq from '../rmq'; import * as rmq from '../rmq';
import { codeFingerprint } from '../../utils'; import { codeFingerprint } from '../../utils';
import { CompileResult, TaskResult, RPCTaskType, RPCRequest, CompileTask, FileContent } from '../../interfaces'; import { CompilationResult, TestcaseResult, RPCTaskType, TaskStatus, RPCRequest, CompileTask, FileContent } from '../../interfaces';
import winston = require('winston');
export async function compile( export async function compile(
code: string, language: Language, extraFiles: FileContent[] = [], priority: number code: string, language: Language, extraFiles: FileContent[] = [], priority: number
): Promise<[string, CompileResult]> { ): Promise<[string, CompilationResult]> {
const fingerprint = codeFingerprint(code, language.name); const fingerprint = codeFingerprint(code, language.name);
let result: CompileResult; winston.debug(`Compiling code, fingerprint = ${fingerprint}`);
let result: CompilationResult;
const unlock = await redis.getCompileLock(fingerprint); const unlock = await redis.getCompileLock(fingerprint);
winston.debug(`Got redis lock for ${fingerprint}`);
try { try {
if (await redis.checkBinaryExistance(fingerprint)) { if (await redis.checkBinaryExistance(fingerprint)) {
result = { status: 0 }; winston.debug('Binary already exists. Exiting');
result = { status: TaskStatus.Done };
} else { } else {
const task: CompileTask = { const task: CompileTask = {
code: code, code: code,

62
src/daemon/judge/index.ts

@ -1,17 +1,42 @@
import { JudgeTask, ProblemType, TestData, StandardJudgeParameter } from '../interfaces'; import { JudgeTask, ProblemType, TestData, StandardJudgeParameter } from '../interfaces';
import { judgeStandard } from './standard'; import { StandardJudger } from './standard';
import {JudgeResult, ErrorType} from '../../interfaces'; import { JudgerBase } from './judger-base';
import { JudgeResult, ErrorType, OverallResult, CompilationResult, TaskStatus, ProgressReportType } from '../../interfaces';
import { readRulesFile } from '../testData'; import { readRulesFile } from '../testData';
import { filterPath } from '../../utils'; import { filterPath } from '../../utils';
import winston = require('winston'); import winston = require('winston');
import rmq = require('../rmq');
export async function listen() {
await rmq.waitForTask(async (task) => {
let result: OverallResult;
try {
await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Started, progress: null });
result = await judge(task, async (progress) => {
await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Progress, progress: progress });
}, async (compileResult) => {
const cresult = { taskId: task.taskId, type: ProgressReportType.Compiled, progress: compileResult };
await rmq.reportResult(cresult);
await rmq.reportProgress(cresult);
});
} catch (err) {
winston.warn(`Judge error!!! TaskId: ${task.taskId}`, err);
result = { error: ErrorType.SystemError, systemMessage: `An error occurred.\n${err.toString()}` };
}
const resultReport = { taskId: task.taskId, type: ProgressReportType.Finished, progress: result };
await rmq.reportResult(resultReport);
await rmq.reportProgress(resultReport);
});
}
export async function judge( export async function judge(
task: JudgeTask, reportProgress: (p: JudgeResult) => Promise<void> task: JudgeTask,
): Promise<JudgeResult> { reportProgress: (p: OverallResult) => Promise<void>,
reportCompileProgress: (p: CompilationResult) => Promise<void>
): Promise<OverallResult> {
winston.verbose(`Judging ${task.taskId}`); winston.verbose(`Judging ${task.taskId}`);
// Parse test data // Parse test data
let testData: TestData = null; let testData: TestData = null;
try { try {
winston.debug(`Reading rules file for ${task.testData}...`);
testData = await readRulesFile(filterPath(task.testData)); testData = await readRulesFile(filterPath(task.testData));
} catch (err) { } catch (err) {
winston.info(`Error reading test data for ${task.testData}`, err); winston.info(`Error reading test data for ${task.testData}`, err);
@ -22,10 +47,33 @@ export async function judge(
return { error: ErrorType.TestDataError, systemMessage: "Testdata unavailable." }; return { error: ErrorType.TestDataError, systemMessage: "Testdata unavailable." };
} }
// Do things let judger: JudgerBase;
if (task.type === ProblemType.Standard) { if (task.type === ProblemType.Standard) {
return await judgeStandard(testData, task.param as StandardJudgeParameter, task.priority, reportProgress); judger = new StandardJudger(testData, task.param as StandardJudgeParameter, task.priority);
} else { } else {
throw new Error(`Task type not supported`); throw new Error(`Task type not supported`);
} }
try {
winston.debug(`Preprocessing testdata for ${task.testData}...`);
await judger.preprocessTestData();
} catch (err) {
winston.verbose(`Test data ${task.testData} err`, err);
return { error: ErrorType.TestDataError, systemMessage: err.toString() };
}
winston.debug(`Compiling...`);
const compileResult = await judger.compile();
winston.debug(`Reporting compilation progress...`);
await reportCompileProgress(compileResult);
if (compileResult.status !== TaskStatus.Done) {
winston.verbose(`Compilation error: ${compileResult.message}`);
return {
compile: compileResult
};
}
winston.debug(`Judging...`);
const judgeResult = await judger.judge(r => reportProgress({ compile: compileResult, judge: r }));
return { compile: compileResult, judge: judgeResult };
} }

121
src/daemon/judge/judger-base.ts

@ -0,0 +1,121 @@
import { TestData, SubtaskScoringType, TestcaseJudge } from '../interfaces';
import { CompilationResult, JudgeResult, TaskStatus, SubtaskResult, TestcaseDetails } from '../../interfaces';
import { Language } from '../../languages';
import { compile } from './compile';
import winston = require('winston');
import _ = require('lodash');
const globalFullScore = 100;
function calculateSubtaskScore(scoring: SubtaskScoringType, scores: number[]): number {
if (scoring === SubtaskScoringType.Minimum) {
return _.min(scores);
} else if (scoring === SubtaskScoringType.Multiple) {
return _.reduce(scores,
(res, cur) => res * cur, 1);
} else if (scoring === SubtaskScoringType.Summation) {
return _.sum(scores) / scores.length;
}
}
export abstract class JudgerBase {
priority: number;
testData: TestData;
constructor(t: TestData, p: number) {
this.priority = p;
this.testData = t;
}
abstract preprocessTestData(): Promise<void>;
abstract compile(): Promise<CompilationResult>;
async judge(reportProgressResult: (p: JudgeResult) => Promise<void>): Promise<JudgeResult> {
const results: SubtaskResult[] = this.testData.subtasks.map(t => ({
cases: t.cases.map(j => ({
status: TaskStatus.Waiting
})),
status: TaskStatus.Waiting
}));
const reportProgress = function () {
reportProgressResult({ status: TaskStatus.Running, subtasks: results });
}
winston.debug(`Totally ${results.length} subtasks.`);
const judgeTasks: Promise<void>[] = [];
for (let subtaskIndex = 0; subtaskIndex < this.testData.subtasks.length; subtaskIndex++) {
const currentResult = results[subtaskIndex];
const currentTask = this.testData.subtasks[subtaskIndex];
judgeTasks.push((async () => {
// Type minimum is skippable, run one by one
if (currentTask.type !== SubtaskScoringType.Summation) {
let skipped: boolean = true;
for (let index = 0; index < currentTask.cases.length; index++) {
const currentTaskResult = currentResult.cases[index];
if (skipped) {
currentTaskResult.status = TaskStatus.Skipped;
} else {
winston.verbose(`Judging ${subtaskIndex}, case ${index}.`);
let score = 0;
try {
const taskJudge = await this.judgeTestcase(currentTask.cases[index], async () => {
currentTaskResult.status = TaskStatus.Running;
currentResult.status = TaskStatus.Running;
await reportProgress();
});
currentTaskResult.status = TaskStatus.Done;
currentTaskResult.result = taskJudge;
score = taskJudge.scoringRate;
} catch (err) {
currentTaskResult.status = TaskStatus.Failed;
currentTaskResult.errorMessage = err.toString();
winston.warn(`Task runner error: ${err.toString()} (subtask ${subtaskIndex}, case ${index})`);
}
if (score === 0 || score === NaN) {
winston.debug(`Subtask ${subtaskIndex}, case ${index}: zero, skipping the rest.`);
skipped = true;
}
await reportProgress();
}
}
} else {
// Non skippable, run all immediately
const caseTasks: Promise<void>[] = [];
for (let index = 0; index < currentTask.cases.length; index++) {
caseTasks.push((async () => {
const currentTaskResult = currentResult.cases[index];
winston.verbose(`Judging ${subtaskIndex}, case ${index}.`);
try {
currentTaskResult.result = await this.judgeTestcase(currentTask.cases[index], async () => {
currentTaskResult.status = TaskStatus.Running;
currentResult.status = TaskStatus.Running;
await reportProgress();
});
currentTaskResult.status = TaskStatus.Done;
} catch (err) {
currentTaskResult.status = TaskStatus.Failed;
currentTaskResult.errorMessage = err.toString();
winston.warn(`Task runner error: ${err.toString()} (subtask ${subtaskIndex}, case ${index})`);
}
await reportProgress();
})());
}
await Promise.all(caseTasks);
}
if (currentResult.cases.some(c => c.status === TaskStatus.Failed)) {
// If any testcase has failed, the score is invaild.
currentResult.score = NaN;
currentResult.status = TaskStatus.Failed;
} else {
currentResult.score = calculateSubtaskScore(currentTask.type, currentResult.cases.map(c => c.result ? c.result.scoringRate : 0)) * currentTask.score;
currentResult.status = TaskStatus.Done;
}
winston.verbose(`Subtask ${subtaskIndex}, finished`);
})());
}
await Promise.all(judgeTasks);
return { status: TaskStatus.Done, subtasks: results };
}
protected abstract judgeTestcase(curCase: TestcaseJudge, started: () => Promise<void>): Promise<TestcaseDetails>;
}

17
src/daemon/judge/process.ts

@ -1,5 +1,5 @@
import { TestData, StandardJudgeParameter, SubtaskJudge, TestCaseJudge, SubtaskScoringType } from '../interfaces'; import { TestData, StandardJudgeParameter, SubtaskJudge, TestcaseJudge, SubtaskScoringType } from '../interfaces';
import { SubtaskResult, TestCaseDetails, TaskStatus, TestCaseResult, JudgeResult } from '../../interfaces'; import { SubtaskResult, TestcaseDetails, TaskStatus, TestcaseResult, JudgeResult } from '../../interfaces';
import { globalConfig as Cfg } from '../config'; import { globalConfig as Cfg } from '../config';
import winston = require('winston'); import winston = require('winston');
import _ = require('lodash'); import _ = require('lodash');
@ -19,12 +19,13 @@ function calculateSubtaskScore(scoring: SubtaskScoringType, scores: number[]): n
export async function processJudgement( export async function processJudgement(
subtasks: SubtaskJudge[], subtasks: SubtaskJudge[],
reportProgress: (r: SubtaskResult[]) => Promise<void>, reportProgress: (r: SubtaskResult[]) => Promise<void>,
judgeTestCase: (curCase: TestCaseJudge, started: () => Promise<void>) => Promise<TestCaseDetails>, judgeTestcase: (curCase: TestcaseJudge, started: () => Promise<void>) => Promise<TestcaseDetails>,
): Promise<SubtaskResult[]> { ): Promise<SubtaskResult[]> {
const results: SubtaskResult[] = subtasks.map(t => ({ const results: SubtaskResult[] = subtasks.map(t => ({
cases: t.cases.map(j => ({ cases: t.cases.map(j => ({
status: TaskStatus.Waiting status: TaskStatus.Waiting
})) })),
status: TaskStatus.Waiting
})); }));
winston.debug(`Totally ${results.length} subtasks.`); winston.debug(`Totally ${results.length} subtasks.`);
@ -45,8 +46,9 @@ export async function processJudgement(
winston.verbose(`Judging ${subtaskIndex}, case ${index}.`); winston.verbose(`Judging ${subtaskIndex}, case ${index}.`);
let score = 0; let score = 0;
try { try {
const taskJudge = await judgeTestCase(currentTask.cases[index], async () => { const taskJudge = await judgeTestcase(currentTask.cases[index], async () => {
currentTaskResult.status = TaskStatus.Running; currentTaskResult.status = TaskStatus.Running;
currentResult.status = TaskStatus.Running;
await reportProgress(results); await reportProgress(results);
}); });
currentTaskResult.status = TaskStatus.Done; currentTaskResult.status = TaskStatus.Done;
@ -72,8 +74,9 @@ export async function processJudgement(
const currentTaskResult = currentResult.cases[index]; const currentTaskResult = currentResult.cases[index];
winston.verbose(`Judging ${subtaskIndex}, case ${index}.`); winston.verbose(`Judging ${subtaskIndex}, case ${index}.`);
try { try {
currentTaskResult.result = await judgeTestCase(currentTask.cases[index], async () => { currentTaskResult.result = await judgeTestcase(currentTask.cases[index], async () => {
currentTaskResult.status = TaskStatus.Running; currentTaskResult.status = TaskStatus.Running;
currentResult.status = TaskStatus.Running;
await reportProgress(results); await reportProgress(results);
}); });
currentTaskResult.status = TaskStatus.Done; currentTaskResult.status = TaskStatus.Done;
@ -90,8 +93,10 @@ export async function processJudgement(
if (currentResult.cases.some(c => c.status === TaskStatus.Failed)) { if (currentResult.cases.some(c => c.status === TaskStatus.Failed)) {
// If any testcase has failed, the score is invaild. // If any testcase has failed, the score is invaild.
currentResult.score = NaN; currentResult.score = NaN;
currentResult.status = TaskStatus.Failed;
} else { } else {
currentResult.score = calculateSubtaskScore(currentTask.type, currentResult.cases.map(c => c.result ? c.result.scoringRate : 0)) * currentTask.score; currentResult.score = calculateSubtaskScore(currentTask.type, currentResult.cases.map(c => c.result ? c.result.scoringRate : 0)) * currentTask.score;
currentResult.status = TaskStatus.Done;
} }
winston.verbose(`Subtask ${subtaskIndex}, finished`); winston.verbose(`Subtask ${subtaskIndex}, finished`);
})()); })());

144
src/daemon/judge/standard.ts

@ -1,92 +1,92 @@
import { TestData, StandardJudgeParameter, TestCaseJudge } from '../interfaces'; import { TestData, StandardJudgeParameter, TestcaseJudge } from '../interfaces';
import { TaskStatus, ErrorType, TestCaseDetails, JudgeResult, TaskResult, StandardRunTask, StandardRunResult, RPCTaskType } from '../../interfaces'; import { TaskStatus, ErrorType, TestcaseDetails, CompilationResult, JudgeResult, TestcaseResult, StandardRunTask, StandardRunResult, RPCTaskType } from '../../interfaces';
import { globalConfig as Cfg } from '../config'; import { globalConfig as Cfg } from '../config';
import { cloneObject, readFileLength } from '../../utils'; import { cloneObject, readFileLength } from '../../utils';
import { compile } from './compile'; import { compile } from './compile';
import { Language, getLanguage } from '../../languages'; import { Language, getLanguage } from '../../languages';
import { processJudgement } from './process' import { processJudgement } from './process'
import { runTask } from '../rmq'; import { runTask } from '../rmq';
import { JudgerBase } from './judger-base';
import pathLib = require('path'); import pathLib = require('path');
import winston = require('winston'); import winston = require('winston');
export async function judgeStandard( export class StandardJudger extends JudgerBase {
testData: TestData, parameters: StandardJudgeParameter;
param: StandardJudgeParameter, userCodeLanguage: Language;
priority: number, spjExecutableName: string = null;
reportProgress: (progress: JudgeResult) => Promise<void> userCodeExecuableName: string = null;
): Promise<JudgeResult> {
winston.debug("Running standard judging procedure.");
let spjName: string = null; constructor(testData: TestData,
if (testData.spj != null) { param: StandardJudgeParameter,
winston.verbose("Compiling special judge."); priority: number) {
const [spjExecutableName, spjResult] = await compile(testData.spj.sourceCode, testData.spj.language, null, priority); super(testData, priority);
spjName = spjExecutableName; this.parameters = param;
this.userCodeLanguage = getLanguage(param.language);
}
if (spjResult.status !== 0) { async preprocessTestData(): Promise<void> {
winston.verbose("Special judge CE."); if (this.testData.spj != null) {
let message = null; winston.verbose("Compiling special judge.");
if (spjResult.message != null && spjResult.message !== "") { const [spjExecutableName, spjResult] = await compile(this.testData.spj.sourceCode,
message = "===== Special Judge Compilation Message =====" + spjResult.message; this.testData.spj.language, null, this.priority);
if (spjResult.status !== TaskStatus.Done) {
winston.verbose("Special judge CE: " + spjResult.message);
let message = null;
if (spjResult.message != null && spjResult.message !== "") {
message = "===== Special Judge Compilation Message =====" + spjResult.message;
}
throw new Error(message);
} else {
this.spjExecutableName = spjExecutableName;
} }
return { error: ErrorType.TestDataError, systemMessage: message }; } else {
this.spjExecutableName = null;
} }
} }
const language = getLanguage(param.language); async compile(): Promise<CompilationResult> {
winston.verbose("Compiling user program."); const language = getLanguage(this.parameters.language);
const [executableName, compilationResult] = await compile( const [executableName, compilationResult] = await compile(
param.code, this.parameters.code,
language, language,
testData.extraSourceFiles[language.name], this.testData.extraSourceFiles[language.name],
priority this.priority
); );
this.userCodeExecuableName = executableName;
if (compilationResult.status !== 0) { return compilationResult;
winston.verbose("User program CE.");
let message = null;
if (compilationResult.message != null && compilationResult.message !== "") {
message = compilationResult.message;
}
return { compileStatus: TaskStatus.Failed, compilerMessage: message };
} }
winston.debug("Start judgement."); async judgeTestcase(curCase: TestcaseJudge, started: () => Promise<void>): Promise<TestcaseDetails> {
return { const task: StandardRunTask = {
subtasks: await processJudgement( testDataName: this.testData.name,
testData.subtasks, inputData: curCase.input,
async (result) => { reportProgress({ subtasks: result }); }, answerData: curCase.output,
async (curCase, st) => { time: this.parameters.timeLimit,
const task: StandardRunTask = { memory: this.parameters.memoryLimit,
testDataName: testData.name, fileIOInput: this.parameters.fileIOInput,
inputData: curCase.input, fileIOOutput: this.parameters.fileIOOutput,
answerData: curCase.output, userExecutableName: this.userCodeExecuableName,
time: param.timeLimit, spjExecutableName: this.spjExecutableName
memory: param.memoryLimit, };
fileIOInput: param.fileIOInput,
fileIOOutput: param.fileIOOutput,
userExecutableName: executableName,
spjExecutableName: testData.spj ? spjName : null,
};
const [inputContent, outputContent, runResult]: [string, string, StandardRunResult] = await Promise.all([ const [inputContent, outputContent, runResult]: [string, string, StandardRunResult] = await Promise.all([
readFileLength(pathLib.join(Cfg.testDataDirectory, testData.name, curCase.input), Cfg.dataDisplayLimit), readFileLength(pathLib.join(Cfg.testDataDirectory, this.testData.name, curCase.input), Cfg.dataDisplayLimit),
readFileLength(pathLib.join(Cfg.testDataDirectory, testData.name, curCase.output), Cfg.dataDisplayLimit), readFileLength(pathLib.join(Cfg.testDataDirectory, this.testData.name, curCase.output), Cfg.dataDisplayLimit),
runTask({ type: RPCTaskType.RunStandard, task: task }, priority, st) runTask({ type: RPCTaskType.RunStandard, task: task }, this.priority, started)
]) as any; ]) as any;
return { return {
type: runResult.result, type: runResult.result,
time: runResult.time, time: runResult.time,
memory: runResult.memory, memory: runResult.memory,
userError: runResult.userError, userError: runResult.userError,
userOutput: runResult.userOutput, userOutput: runResult.userOutput,
scoringRate: runResult.scoringRate, scoringRate: runResult.scoringRate,
spjMessage: runResult.spjMessage, spjMessage: runResult.spjMessage,
input: { name: curCase.input, content: inputContent }, input: { name: curCase.input, content: inputContent },
output: { name: curCase.output, content: outputContent }, output: { name: curCase.output, content: outputContent },
systemMessage: runResult.systemMessage systemMessage: runResult.systemMessage
}; };
}) }
};
} }

22
src/daemon/rmq.ts

@ -36,33 +36,17 @@ async function newChannel(): Promise<amqp.Channel> {
} }
export async function waitForTask(handle: (task: JudgeTask) => Promise<void>) { export async function waitForTask(handle: (task: JudgeTask) => Promise<void>) {
const channel = await newChannel(); await rmqCommon.waitForTask(amqpConnection, rmqCommon.judgeQueueName, Cfg.priority, () => false, handle);
channel.prefetch(1);
await channel.consume(rmqCommon.judgeQueueName, (msg: amqp.Message) => {
const messageId = msg.properties.messageId;
winston.info(`Got judge task`);
(async () => {
const data = msgpack.decode(msg.content) as JudgeTask;
winston.debug(`Data: ${util.inspect(data)}`);
await handle(data);
})().then(async () => {
channel.ack(msg);
}, async (err) => {
// Do not requeue it.
winston.warn(`Failed to process message ${messageId}: ${err.toString()}`);
channel.nack(msg, false, false);
});
}, {
priority: Cfg.priority
});
} }
export async function reportProgress(data: ProgressReportData) { export async function reportProgress(data: ProgressReportData) {
winston.verbose('Reporting progress', data);
const payload = msgpack.encode(data); const payload = msgpack.encode(data);
publicChannel.publish(rmqCommon.progressExchangeName, '', payload); publicChannel.publish(rmqCommon.progressExchangeName, '', payload);
} }
export async function reportResult(data: ProgressReportData) { export async function reportResult(data: ProgressReportData) {
winston.verbose('Reporting result', data);
const payload = msgpack.encode(data); const payload = msgpack.encode(data);
publicChannel.sendToQueue(rmqCommon.resultReportQueueName, payload); publicChannel.sendToQueue(rmqCommon.resultReportQueueName, payload);
} }

2
src/daemon/testData.ts

@ -3,7 +3,7 @@ import fse = require('fs-extra');
import pathLib = require('path'); import pathLib = require('path');
import { Language, languages, getLanguage } from '../languages'; import { Language, languages, getLanguage } from '../languages';
import { compareStringByNumber, tryReadFile, filterPath } from '../utils'; import { compareStringByNumber, tryReadFile, filterPath } from '../utils';
import { SubtaskScoringType, SubtaskJudge, TestCaseJudge, Executable, TestData } from './interfaces'; import { SubtaskScoringType, SubtaskJudge, TestcaseJudge, Executable, TestData } from './interfaces';
import { FileContent } from '../interfaces'; import { FileContent } from '../interfaces';
import { globalConfig as Cfg } from './config'; import { globalConfig as Cfg } from './config';

43
src/interfaces.ts

@ -17,8 +17,8 @@ export interface CompileTask {
binaryName: string; binaryName: string;
} }
export interface TestCaseDetails { export interface TestcaseDetails {
type: TaskResult; type: TestcaseResultType;
time: number; time: number;
memory: number; memory: number;
input: FileContent; input: FileContent;
@ -30,15 +30,16 @@ export interface TestCaseDetails {
systemMessage: string; systemMessage: string;
}; };
export interface TestCaseResult { export interface TestcaseResult {
status: TaskStatus; status: TaskStatus;
result?: TestCaseDetails; result?: TestcaseDetails;
errorMessage?: string; errorMessage?: string;
} }
export interface SubtaskResult { export interface SubtaskResult {
status: TaskStatus;
score?: number; score?: number;
cases: TestCaseResult[]; cases: TestcaseResult[];
} }
export enum ErrorType { export enum ErrorType {
@ -46,12 +47,21 @@ export enum ErrorType {
TestDataError TestDataError
} }
export interface CompilationResult {
status: TaskStatus;
message?: string;
}
export interface JudgeResult { export interface JudgeResult {
error?: ErrorType; status: TaskStatus;
compileStatus?: TaskStatus;
subtasks?: SubtaskResult[]; subtasks?: SubtaskResult[];
compilerMessage?: string; }
export interface OverallResult {
error?: ErrorType;
systemMessage?: string; systemMessage?: string;
compile?: CompilationResult;
judge?: JudgeResult;
} }
export interface StandardRunResult { export interface StandardRunResult {
@ -62,7 +72,7 @@ export interface StandardRunResult {
scoringRate: number; scoringRate: number;
spjMessage: string; spjMessage: string;
systemMessage: string; systemMessage: string;
result: TaskResult; result: TestcaseResultType;
} }
export interface StandardRunTask { export interface StandardRunTask {
@ -85,7 +95,7 @@ export enum TaskStatus {
Skipped = 4 Skipped = 4
} }
export enum TaskResult { export enum TestcaseResultType {
Accepted = 1, Accepted = 1,
WrongAnswer, WrongAnswer,
PartiallyCorrect, PartiallyCorrect,
@ -98,12 +108,6 @@ export enum TaskResult {
InvalidInteraction InvalidInteraction
} }
export interface CompileResult {
// -1: Run failed, 0: OK, others: Compilation Error
status: number;
message?: string;
}
export interface FileContent { export interface FileContent {
content: string, content: string,
name: string name: string
@ -117,14 +121,15 @@ export enum RPCReplyType {
export enum ProgressReportType { export enum ProgressReportType {
Started = 1, Started = 1,
Progress = 2, Compiled = 2,
Finished = 3 Progress = 3,
Finished = 4,
} }
export interface ProgressReportData { export interface ProgressReportData {
taskId: number; taskId: number;
type: ProgressReportType; type: ProgressReportType;
progress: JudgeResult; progress: OverallResult | CompilationResult;
} }
export interface RPCReply { export interface RPCReply {

50
src/judgeResult.ts

@ -1,5 +1,6 @@
import _ = require('lodash'); import _ = require('lodash');
import winston = require('winston'); import winston = require('winston');
import { JudgeResult, OverallResult, TestcaseResultType, TaskStatus, ErrorType, SubtaskResult, TestcaseResult, TestcaseDetails } from './interfaces';
export interface JudgeResultSubmit { export interface JudgeResultSubmit {
taskId: number; taskId: number;
@ -8,36 +9,34 @@ export interface JudgeResultSubmit {
score: number; score: number;
statusNumber: number; statusNumber: number;
statusString: string; statusString: string;
result: JudgeResult; result: OverallResult;
} }
import { JudgeResult, TaskResult, TaskStatus, ErrorType, SubtaskResult, TestCaseResult, TestCaseDetails } from './interfaces';
const compileError = "Compile Error", const compileError = "Compile Error",
systemError = "System Error", systemError = "System Error",
testdataError = "No Testdata"; testdataError = "No Testdata";
export const statusToString = {}; export const statusToString = {};
statusToString[TaskResult.Accepted] = "Accepted"; statusToString[TestcaseResultType.Accepted] = "Accepted";
statusToString[TaskResult.WrongAnswer] = "Wrong Answer"; statusToString[TestcaseResultType.WrongAnswer] = "Wrong Answer";
statusToString[TaskResult.PartiallyCorrect] = "Partially Correct"; statusToString[TestcaseResultType.PartiallyCorrect] = "Partially Correct";
statusToString[TaskResult.MemoryLimitExceeded] = "Memory Limit Exceeded"; statusToString[TestcaseResultType.MemoryLimitExceeded] = "Memory Limit Exceeded";
statusToString[TaskResult.TimeLimitExceeded] = "Time Limit Exceeded"; statusToString[TestcaseResultType.TimeLimitExceeded] = "Time Limit Exceeded";
statusToString[TaskResult.OutputLimitExceeded] = "Output Limit Exceeded"; statusToString[TestcaseResultType.OutputLimitExceeded] = "Output Limit Exceeded";
statusToString[TaskResult.RuntimeError] = "Runtime Error"; statusToString[TestcaseResultType.RuntimeError] = "Runtime Error";
statusToString[TaskResult.FileError] = "File Error"; statusToString[TestcaseResultType.FileError] = "File Error";
statusToString[TaskResult.JudgementFailed] = "Judgement Failed"; statusToString[TestcaseResultType.JudgementFailed] = "Judgement Failed";
statusToString[TaskResult.InvalidInteraction] = "Invalid Interaction"; statusToString[TestcaseResultType.InvalidInteraction] = "Invalid Interaction";
export function firstNonAC(t: TaskResult[]): TaskResult { export function firstNonAC(t: TestcaseResultType[]): TestcaseResultType {
if (t.every(v => v === TaskResult.Accepted)) { if (t.every(v => v === TestcaseResultType.Accepted)) {
return TaskResult.Accepted return TestcaseResultType.Accepted
} else { } else {
return t.find(r => r !== TaskResult.Accepted); return t.find(r => r !== TestcaseResultType.Accepted);
} }
} }
export function convertResult(id: number, source: JudgeResult): JudgeResultSubmit { export function convertResult(id: number, source: OverallResult): JudgeResultSubmit {
winston.debug(`Converting result for ${id}`, source); winston.debug(`Converting result for ${id}`, source);
let time = -1, let time = -1,
memory = -1, memory = -1,
@ -45,7 +44,7 @@ export function convertResult(id: number, source: JudgeResult): JudgeResultSubmi
done = true, done = true,
statusString = null; statusString = null;
if (source.compileStatus === TaskStatus.Failed) { if (source.compile && source.compile.status === TaskStatus.Failed) {
statusString = compileError; statusString = compileError;
score = 0; score = 0;
} else if (source.error != null) { } else if (source.error != null) {
@ -56,21 +55,24 @@ export function convertResult(id: number, source: JudgeResult): JudgeResultSubmi
} else { } else {
statusString = systemError; statusString = systemError;
} }
} else if (source.subtasks != null) { } else if (source.judge != null && source.judge.subtasks != null) {
if (source.subtasks.some(s => s.score === NaN)) { if (source.judge.subtasks.some(s => s.status === TaskStatus.Failed)) {
winston.debug(`Some subtasks failed, returning system error`);
score = NaN; score = NaN;
statusString = systemError; statusString = systemError;
} else { } else {
score = _.sum(source.subtasks.map(s => s.score)); score = _.sum(source.judge.subtasks.map(s => s.score));
const forEveryTestcase = function <TParam>(map: (v: TestCaseDetails) => TParam, reduce: (v: TParam[]) => TParam): TParam { const forEveryTestcase = function <TParam>(map: (v: TestcaseDetails) => TParam, reduce: (v: TParam[]) => TParam): TParam {
return reduce(source.subtasks.map(s => reduce(s.cases.filter(c => c.result != null).map(c => map(c.result))))); return reduce(source.judge.subtasks.map(s => reduce(s.cases.filter(c => c.result != null).map(c => map(c.result)))));
} }
time = forEveryTestcase(c => c.time, _.sum); time = forEveryTestcase(c => c.time, _.sum);
memory = forEveryTestcase(c => c.memory, _.max); memory = forEveryTestcase(c => c.memory, _.max);
const finalResult = forEveryTestcase(c => c.type, firstNonAC); const finalResult = forEveryTestcase(c => c.type, firstNonAC);
statusString = statusToString[finalResult]; statusString = statusToString[finalResult];
} }
} else {
statusString = systemError;
} }
const result = { const result = {

29
src/rmq-common.ts

@ -1,4 +1,6 @@
import amqp = require('amqplib'); import amqp = require('amqplib');
import msgpack = require('msgpack-lite');
import winston = require('winston');
export const maxPriority = 5; export const maxPriority = 5;
export const taskQueueName = 'task'; export const taskQueueName = 'task';
@ -12,6 +14,10 @@ export async function assertTaskQueue(channel: amqp.Channel) {
}); });
} }
// Difference between result and progress:
// The `progress' is to be handled by *all* frontend proxies and pushed to all clients.
// The `result' is to be handled only *once*, and is to be written to the database.
export async function assertProgressReportExchange(channel: amqp.Channel) { export async function assertProgressReportExchange(channel: amqp.Channel) {
await channel.assertExchange(progressExchangeName, 'fanout', { durable: false }); await channel.assertExchange(progressExchangeName, 'fanout', { durable: false });
} }
@ -25,4 +31,25 @@ export async function assertJudgeQueue(channel: amqp.Channel) {
maxPriority: maxPriority, maxPriority: maxPriority,
durable: true durable: true
}); });
} }
export async function waitForTask<T>(conn: amqp.Connection, queueName: string, priority: number, retry: (err: Error) => boolean, handle: (task: T) => Promise<void>) {
const channel = await conn.createChannel();
channel.prefetch(1);
await channel.consume(queueName, (msg: amqp.Message) => {
const data = msgpack.decode(msg.content) as T;
winston.verbose('Got task', data);
handle(data).then(async () => {
channel.ack(msg);
}, async (err) => {
if (retry)
await new Promise((res) => setTimeout(res, 300));
winston.warn(`Failed to process message: ${err.toString()}`);
channel.nack(msg, false, retry(err));
});
}, {
priority: priority
});
}

14
src/runner/compile.ts

@ -4,7 +4,7 @@ import randomString = require('randomstring');
import bluebird = require('bluebird'); import bluebird = require('bluebird');
import getFolderSize = require('get-folder-size'); import getFolderSize = require('get-folder-size');
import { CompileTask, CompileResult } from '../interfaces'; import { CompileTask, CompilationResult, TaskStatus } from '../interfaces';
import { globalConfig as Cfg } from './config'; import { globalConfig as Cfg } from './config';
import { sandboxize, createOrEmptyDir, setWriteAccess } from './utils'; import { sandboxize, createOrEmptyDir, setWriteAccess } from './utils';
import { Language, getLanguage } from '../languages'; import { Language, getLanguage } from '../languages';
@ -15,7 +15,7 @@ import { pushBinary } from './executable';
const getSize: any = bluebird.promisify(getFolderSize); const getSize: any = bluebird.promisify(getFolderSize);
export async function compile(task: CompileTask): Promise<CompileResult> { export async function compile(task: CompileTask): Promise<CompilationResult> {
const srcDir = pathLib.join(Cfg.workingDirectory, `src`); const srcDir = pathLib.join(Cfg.workingDirectory, `src`);
const binDir = pathLib.join(Cfg.workingDirectory, `bin`); const binDir = pathLib.join(Cfg.workingDirectory, `bin`);
const tempDir = pathLib.join(Cfg.workingDirectory, 'temp'); const tempDir = pathLib.join(Cfg.workingDirectory, 'temp');
@ -68,25 +68,25 @@ export async function compile(task: CompileTask): Promise<CompileResult> {
// If the output is too long // If the output is too long
if (outputSize > language.binarySizeLimit) { if (outputSize > language.binarySizeLimit) {
return { return {
status: -1, status: TaskStatus.Failed,
message: `Your source code compiled to ${outputSize} bytes which is too big, too thick, too long for us..` message: `Your source code compiled to ${outputSize} bytes which is too big, too thick, too long for us..`
}; };
} // Else OK! } // Else OK!
} else { // If compilation error } else { // If compilation error
return { return {
status: sandboxResult.code, status: TaskStatus.Failed,
message: await readFileLength(binDir + '/' + compileConfig.messageFile, Cfg.compilerMessageLimit) message: await readFileLength(pathLib.join(binDir, compileConfig.messageFile), Cfg.compilerMessageLimit)
}; };
} }
} else { } else {
return { return {
status: -1, status: TaskStatus.Failed,
message: (`A ${SandboxStatus[sandboxResult.status]} encountered while compiling your code.\n\n` + await readFileLength(binDir + '/' + compileConfig.messageFile, Cfg.compilerMessageLimit)).trim() message: (`A ${SandboxStatus[sandboxResult.status]} encountered while compiling your code.\n\n` + await readFileLength(binDir + '/' + compileConfig.messageFile, Cfg.compilerMessageLimit)).trim()
}; };
} }
await pushBinary(task.binaryName, language, task.code, binDir); await pushBinary(task.binaryName, language, task.code, binDir);
return { status: 0 }; return { status: TaskStatus.Done };
} finally { } finally {
await Promise.all([fse.remove(binDir), fse.remove(srcDir)]); await Promise.all([fse.remove(binDir), fse.remove(srcDir)]);
} }

13
src/runner/config.ts

@ -1,6 +1,7 @@
import * as commandLineArgs from 'command-line-args'; import commandLineArgs = require('command-line-args');
import * as fs from 'fs'; import fs = require('fs');
import * as winston from 'winston'; import winston = require('winston');
import { configureWinston } from '../winston-common';
export interface SandboxConfigBase { export interface SandboxConfigBase {
chroot: string; chroot: string;
@ -69,8 +70,4 @@ export const globalConfig: ConfigStructure = {
}, },
} }
if (options.verbose) { configureWinston(options.verbose);
(winston as any).level = 'debug';
} else {
(winston as any).level = 'warn';
}

3
src/runner/executable.ts

@ -29,7 +29,7 @@ export async function pushBinary(name: string, language: Language, code: string,
language: language.name, language: language.name,
code: code code: code
}; };
await putRedis(name + redisBinarySuffix, msgpack.encode(binary)); await putRedis(name + redisBinarySuffix, binary);
await putRedis(name + redisMetadataSuffix, msgpack.encode(data)); await putRedis(name + redisMetadataSuffix, msgpack.encode(data));
} }
@ -57,6 +57,7 @@ export async function fetchBinary(name: string): Promise<[string, Language, stri
winston.debug(`Doing work: fetching binary for ${name} ...`); winston.debug(`Doing work: fetching binary for ${name} ...`);
await fse.mkdir(targetName); await fse.mkdir(targetName);
const binary = msgpack.decode(await getRedis(name + redisBinarySuffix)); const binary = msgpack.decode(await getRedis(name + redisBinarySuffix));
winston.debug(`Decompressing binary (size=${binary.length})...`);
await new Promise((res, rej) => { await new Promise((res, rej) => {
const s = tar.extract({ const s = tar.extract({
cwd: targetName cwd: targetName

32
src/runner/judge.ts

@ -4,7 +4,7 @@ import fse = require('fs-extra');
import winston = require('winston'); import winston = require('winston');
import { SandboxStatus } from 'simple-sandbox/lib/interfaces'; import { SandboxStatus } from 'simple-sandbox/lib/interfaces';
import { TaskResult, StandardRunTask, StandardRunResult } from '../interfaces'; import { TestcaseResultType, StandardRunTask, StandardRunResult } from '../interfaces';
import { createOrEmptyDir, tryEmptyDir } from './utils'; import { createOrEmptyDir, tryEmptyDir } from './utils';
import { readFileLength, tryReadFile } from '../utils'; import { readFileLength, tryReadFile } from '../utils';
import { globalConfig as Cfg } from './config'; import { globalConfig as Cfg } from './config';
@ -17,7 +17,7 @@ const workingDir = `${Cfg.workingDirectory}/data`;
const spjWorkingDir = `${Cfg.workingDirectory}/data-spj`; const spjWorkingDir = `${Cfg.workingDirectory}/data-spj`;
interface SpjResult { interface SpjResult {
status: TaskResult; status: TestcaseResultType;
message: string; message: string;
score: number; score: number;
} }
@ -37,7 +37,7 @@ async function runSpj(spjBinDir: string, spjLanguage: Language): Promise<SpjResu
if (spjRunResult.result.status !== SandboxStatus.OK) { if (spjRunResult.result.status !== SandboxStatus.OK) {
return { return {
status: TaskResult.JudgementFailed, status: TestcaseResultType.JudgementFailed,
message: `Special Judge ${SandboxStatus[spjRunResult.result.status]} encouneted.`, message: `Special Judge ${SandboxStatus[spjRunResult.result.status]} encouneted.`,
score: 0 score: 0
}; };
@ -48,21 +48,21 @@ async function runSpj(spjBinDir: string, spjLanguage: Language): Promise<SpjResu
if ((!scoreString) || score === NaN || score < 0 || score > spjFullScore) { if ((!scoreString) || score === NaN || score < 0 || score > spjFullScore) {
return { return {
status: TaskResult.JudgementFailed, status: TestcaseResultType.JudgementFailed,
message: `Special Judge returned an unrecoginzed score: ${scoreString}.`, message: `Special Judge returned an unrecoginzed score: ${scoreString}.`,
score: 0 score: 0
}; };
} else { } else {
let status: TaskResult; let status: TestcaseResultType;
switch (score) { switch (score) {
case spjFullScore: case spjFullScore:
status = TaskResult.Accepted; status = TestcaseResultType.Accepted;
break; break;
case 0: case 0:
status = TaskResult.WrongAnswer; status = TestcaseResultType.WrongAnswer;
break; break;
default: default:
status = TaskResult.PartiallyCorrect; status = TestcaseResultType.PartiallyCorrect;
break; break;
} }
return { return {
@ -131,18 +131,18 @@ export async function judgeStandard(task: StandardRunTask): Promise<StandardRunR
const time = Math.round(runResult.result.time / 1e6), const time = Math.round(runResult.result.time / 1e6),
memory = runResult.result.memory / 1024; memory = runResult.result.memory / 1024;
let status = null, message = null; let status: TestcaseResultType = null, message = null;
if (runResult.outputLimitExceeded) { if (runResult.outputLimitExceeded) {
status = TaskResult.OutputLimitExceeded; status = TestcaseResultType.OutputLimitExceeded;
} else if (runResult.result.status === SandboxStatus.TimeLimitExceeded) { } else if (runResult.result.status === SandboxStatus.TimeLimitExceeded) {
status = TaskResult.TimeLimitExceeded; status = TestcaseResultType.TimeLimitExceeded;
} else if (runResult.result.status === SandboxStatus.MemoryLimitExceeded) { } else if (runResult.result.status === SandboxStatus.MemoryLimitExceeded) {
status = TaskResult.MemoryLimitExceeded; status = TestcaseResultType.MemoryLimitExceeded;
} else if (runResult.result.status === SandboxStatus.RuntimeError) { } else if (runResult.result.status === SandboxStatus.RuntimeError) {
message = `Killed: ${signals[runResult.result.code]}`; message = `Killed: ${signals[runResult.result.code]}`;
status = TaskResult.RuntimeError; status = TestcaseResultType.RuntimeError;
} else if (runResult.result.status !== SandboxStatus.OK) { } else if (runResult.result.status !== SandboxStatus.OK) {
status = TaskResult.RuntimeError; status = TestcaseResultType.RuntimeError;
} else { } else {
message = `Exited with return code ${runResult.result.code}`; message = `Exited with return code ${runResult.result.code}`;
} }
@ -156,7 +156,7 @@ export async function judgeStandard(task: StandardRunTask): Promise<StandardRunR
await fse.move(pathLib.join(workingDir, outputFileName), pathLib.join(spjWorkingDir, 'user_out')); await fse.move(pathLib.join(workingDir, outputFileName), pathLib.join(spjWorkingDir, 'user_out'));
} catch (e) { } catch (e) {
if (e.code === 'ENOENT' && runResult.result.status === SandboxStatus.OK) { if (e.code === 'ENOENT' && runResult.result.status === SandboxStatus.OK) {
status = TaskResult.FileError; status = TestcaseResultType.FileError;
} }
} }
@ -192,7 +192,7 @@ export async function judgeStandard(task: StandardRunTask): Promise<StandardRunR
return Object.assign({ return Object.assign({
scoringRate: diffResult.pass ? 1 : 0, scoringRate: diffResult.pass ? 1 : 0,
spjMessage: diffResult.message, spjMessage: diffResult.message,
result: diffResult.pass ? TaskResult.Accepted : TaskResult.WrongAnswer, result: diffResult.pass ? TestcaseResultType.Accepted : TestcaseResultType.WrongAnswer,
}, partialResult); }, partialResult);
} }
} }

20
src/winston-common.ts

@ -0,0 +1,20 @@
import winston = require('winston');
import _ = require('lodash');
function formatter(args) {
var msg = args.level + ' - ' + args.message + (_.isEmpty(args.meta) ? '' : (' - ' + JSON.stringify(args.meta)));
return msg;
}
export function configureWinston(verbose: boolean) {
winston.configure({
transports: [
new (winston.transports.Console)({ formatter: formatter })
]
});
if (verbose) {
(winston as any).level = 'debug';
} else {
(winston as any).level = 'warn';
}
}
Loading…
Cancel
Save