Browse Source

Add support for answer-submission problems.

master
t123yh 7 years ago
parent
commit
5d6ffb32d5
  1. 3
      daemon-config-example.json
  2. 305
      package-lock.json
  3. 1
      package.json
  4. 8
      src/daemon-frontend-syzoj/daemonRouter.ts
  5. 4
      src/daemon/config.ts
  6. 12
      src/daemon/index.ts
  7. 13
      src/daemon/interfaces.ts
  8. 38
      src/daemon/judge/index.ts
  9. 4
      src/daemon/judge/judger-base.ts
  10. 101
      src/daemon/judge/submit-answer.ts
  11. 29
      src/interfaces.ts
  12. 9
      src/runner/index.ts
  13. 54
      src/runner/judge.ts
  14. 11
      src/utils.ts

3
daemon-config-example.json

@ -3,5 +3,6 @@
"RedisUrl": "redis://127.0.0.1:6379", "RedisUrl": "redis://127.0.0.1:6379",
"TestData": "/home/t123yh/sync2/syzoj/uploads/testdata", "TestData": "/home/t123yh/sync2/syzoj/uploads/testdata",
"Priority": 1, "Priority": 1,
"DataDisplayLimit": 100 "DataDisplayLimit": 100,
"TempDirectory": "/tmp"
} }

305
package-lock.json generated

@ -329,6 +329,11 @@
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
}, },
"base64-js": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
"integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg="
},
"base64id": { "base64id": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
@ -369,6 +374,38 @@
"buffer-more-ints": "0.0.2" "buffer-more-ints": "0.0.2"
} }
}, },
"bl": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
"integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
"requires": {
"readable-stream": "2.3.3"
},
"dependencies": {
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"util-deprecate": "1.0.2"
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"requires": {
"safe-buffer": "5.1.1"
}
}
}
},
"blob": { "blob": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
@ -404,6 +441,21 @@
"hoek": "2.16.3" "hoek": "2.16.3"
} }
}, },
"buffer": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz",
"integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=",
"requires": {
"base64-js": "0.0.8",
"ieee754": "1.1.8",
"isarray": "1.0.0"
}
},
"buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
},
"buffer-equal-constant-time": { "buffer-equal-constant-time": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
@ -474,6 +526,14 @@
"typical": "2.6.1" "typical": "2.6.1"
} }
}, },
"commander": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
"requires": {
"graceful-readlink": "1.0.1"
}
},
"component-bind": { "component-bind": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
@ -564,6 +624,78 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"decompress": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz",
"integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=",
"requires": {
"decompress-tar": "4.1.1",
"decompress-tarbz2": "4.1.1",
"decompress-targz": "4.1.1",
"decompress-unzip": "4.0.1",
"graceful-fs": "4.1.11",
"make-dir": "1.0.0",
"pify": "2.3.0",
"strip-dirs": "2.0.0"
}
},
"decompress-tar": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz",
"integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==",
"requires": {
"file-type": "5.2.0",
"is-stream": "1.1.0",
"tar-stream": "1.5.4"
}
},
"decompress-tarbz2": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz",
"integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==",
"requires": {
"decompress-tar": "4.1.1",
"file-type": "6.1.0",
"is-stream": "1.1.0",
"seek-bzip": "1.0.5",
"unbzip2-stream": "1.2.5"
},
"dependencies": {
"file-type": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-6.1.0.tgz",
"integrity": "sha1-Wn26mBOPoKvsevxD5amgsqrHKfE="
}
}
},
"decompress-targz": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz",
"integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==",
"requires": {
"decompress-tar": "4.1.1",
"file-type": "5.2.0",
"is-stream": "1.1.0"
}
},
"decompress-unzip": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz",
"integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=",
"requires": {
"file-type": "3.9.0",
"get-stream": "2.3.1",
"pify": "2.3.0",
"yauzl": "2.8.0"
},
"dependencies": {
"file-type": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
}
}
},
"delayed-stream": { "delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -612,6 +744,14 @@
"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="
}, },
"end-of-stream": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz",
"integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=",
"requires": {
"once": "1.4.0"
}
},
"engine.io": { "engine.io": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.0.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.0.tgz",
@ -752,6 +892,19 @@
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
}, },
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
"requires": {
"pend": "1.2.0"
}
},
"file-type": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz",
"integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY="
},
"finalhandler": { "finalhandler": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz",
@ -846,6 +999,15 @@
} }
} }
}, },
"get-stream": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
"integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=",
"requires": {
"object-assign": "4.1.1",
"pinkie-promise": "2.0.1"
}
},
"getpass": { "getpass": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@ -866,6 +1028,11 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
}, },
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
},
"har-schema": { "har-schema": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
@ -982,6 +1149,16 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz",
"integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=" "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA="
}, },
"is-natural-number": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz",
"integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg="
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"is-typedarray": { "is-typedarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@ -1141,6 +1318,14 @@
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
}, },
"make-dir": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz",
"integrity": "sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg=",
"requires": {
"pify": "2.3.0"
}
},
"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",
@ -1264,6 +1449,14 @@
"ee-first": "1.1.1" "ee-first": "1.1.1"
} }
}, },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1.0.2"
}
},
"parsejson": { "parsejson": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz",
@ -1298,11 +1491,34 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
}, },
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
},
"performance-now": { "performance-now": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU="
}, },
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
},
"pinkie": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
},
"pinkie-promise": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
"requires": {
"pinkie": "2.0.4"
}
},
"posix": { "posix": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/posix/-/posix-4.1.1.tgz", "resolved": "https://registry.npmjs.org/posix/-/posix-4.1.1.tgz",
@ -1311,6 +1527,11 @@
"nan": "2.4.0" "nan": "2.4.0"
} }
}, },
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
},
"proxy-addr": { "proxy-addr": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz",
@ -1452,6 +1673,14 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
}, },
"seek-bzip": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz",
"integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=",
"requires": {
"commander": "2.8.1"
}
},
"send": { "send": {
"version": "0.15.4", "version": "0.15.4",
"resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz",
@ -1663,6 +1892,14 @@
"ansi-regex": "0.2.1" "ansi-regex": "0.2.1"
} }
}, },
"strip-dirs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.0.0.tgz",
"integrity": "sha1-YQzbKSggDaAAT0HcuQ/JXNkZoLY=",
"requires": {
"is-natural-number": "4.0.1"
}
},
"supports-color": { "supports-color": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
@ -1680,6 +1917,41 @@
"yallist": "3.0.2" "yallist": "3.0.2"
} }
}, },
"tar-stream": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz",
"integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=",
"requires": {
"bl": "1.2.1",
"end-of-stream": "1.4.0",
"readable-stream": "2.3.3",
"xtend": "4.0.1"
},
"dependencies": {
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"util-deprecate": "1.0.2"
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"requires": {
"safe-buffer": "5.1.1"
}
}
}
},
"test-value": { "test-value": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz",
@ -1699,6 +1971,11 @@
} }
} }
}, },
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"to-array": { "to-array": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
@ -1759,6 +2036,15 @@
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz",
"integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ=" "integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ="
}, },
"unbzip2-stream": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz",
"integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==",
"requires": {
"buffer": "3.6.0",
"through": "2.3.8"
}
},
"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",
@ -1769,6 +2055,11 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
}, },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"utils-merge": { "utils-merge": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
@ -1820,6 +2111,11 @@
"stack-trace": "0.0.10" "stack-trace": "0.0.10"
} }
}, },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": { "ws": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
@ -1851,6 +2147,15 @@
"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="
}, },
"yauzl": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz",
"integrity": "sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI=",
"requires": {
"buffer-crc32": "0.2.13",
"fd-slicer": "1.0.1"
}
},
"yeast": { "yeast": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",

1
package.json

@ -13,6 +13,7 @@
"command-line-args": "^4.0.7", "command-line-args": "^4.0.7",
"cors": "^2.8.4", "cors": "^2.8.4",
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"decompress": "^4.2.0",
"express": "^4.15.4", "express": "^4.15.4",
"fs-extra": "^3.0.1", "fs-extra": "^3.0.1",
"get-folder-size": "^1.0.0", "get-folder-size": "^1.0.0",

8
src/daemon-frontend-syzoj/daemonRouter.ts

@ -10,7 +10,7 @@ const taskRouter: express.Router = express.Router();
interface JudgeTask { interface JudgeTask {
content: any; content: any;
extraDataLocation?: string; extraFileLocation?: string;
} }
taskRouter.use((req, res, next) => { taskRouter.use((req, res, next) => {
@ -29,9 +29,9 @@ taskRouter.put('/task', async (req, res) => {
winston.info("Got task: " + JSON.stringify(req.body)); winston.info("Got task: " + JSON.stringify(req.body));
const task = req.body as JudgeTask; const task = req.body as JudgeTask;
let extraData: Buffer = null; let extraData: Buffer = null;
if (task.extraDataLocation != null) { if (task.extraFileLocation != null) {
winston.verbose("Have extra data, download..."); winston.verbose(`Have extra data, downloading from '${task.extraFileLocation}'...`);
extraData = await rp(urlLib.resolve(Cfg.remoteUrl, task.extraDataLocation), { extraData = await rp(urlLib.resolve(Cfg.remoteUrl, task.extraFileLocation), {
encoding: null, encoding: null,
simple: true simple: true
}); });

4
src/daemon/config.ts

@ -9,6 +9,7 @@ export interface ConfigStructure {
priority: number; priority: number;
redis: string; redis: string;
dataDisplayLimit: number; dataDisplayLimit: number;
tempDirectory: string;
} }
const optionDefinitions = [ const optionDefinitions = [
@ -28,7 +29,8 @@ export const globalConfig: ConfigStructure = {
testDataDirectory: configJSON.TestData, testDataDirectory: configJSON.TestData,
priority: configJSON.Priority, priority: configJSON.Priority,
redis: configJSON.RedisUrl, redis: configJSON.RedisUrl,
dataDisplayLimit: configJSON.DataDisplayLimit dataDisplayLimit: configJSON.DataDisplayLimit,
tempDirectory: configJSON.TempDirectory
} }
configureWinston(options.verbose); configureWinston(options.verbose);

12
src/daemon/index.ts

@ -14,19 +14,19 @@ import { JudgeResult, ErrorType, ProgressReportType, OverallResult } from '../in
await rmq.waitForTask(async (task) => { await rmq.waitForTask(async (task) => {
let result: OverallResult; let result: OverallResult;
try { try {
await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Started, progress: null }); await rmq.reportProgress({ taskId: task.content.taskId, type: ProgressReportType.Started, progress: null });
result = await judge(task, async (progress) => { result = await judge(task.content, task.extraData, async (progress) => {
await rmq.reportProgress({ taskId: task.taskId, type: ProgressReportType.Progress, progress: progress }); await rmq.reportProgress({ taskId: task.content.taskId, type: ProgressReportType.Progress, progress: progress });
}, async (progress) => { }, async (progress) => {
const data = { taskId: task.taskId, type: ProgressReportType.Compiled, progress: progress }; const data = { taskId: task.content.taskId, type: ProgressReportType.Compiled, progress: progress };
await rmq.reportProgress(data); await rmq.reportProgress(data);
await rmq.reportResult(data); await rmq.reportResult(data);
}); });
} catch (err) { } catch (err) {
winston.warn(`Judge error!!! TaskId: ${task.taskId}`, err); winston.warn(`Judge error!!! TaskId: ${task.content.taskId}`, err);
result = { error: ErrorType.SystemError, systemMessage: `An error occurred.\n${err.toString()}` }; result = { error: ErrorType.SystemError, systemMessage: `An error occurred.\n${err.toString()}` };
} }
const resultReport = { taskId: task.taskId, type: ProgressReportType.Finished, progress: result }; const resultReport = { taskId: task.content.taskId, type: ProgressReportType.Finished, progress: result };
await rmq.reportProgress(resultReport); await rmq.reportProgress(resultReport);
await rmq.reportResult(resultReport); await rmq.reportResult(resultReport);
}); });

13
src/daemon/interfaces.ts

@ -7,12 +7,17 @@ export enum ProblemType {
Interaction = 3 Interaction = 3
} }
export interface JudgeTask { export interface JudgeTaskContent {
taskId: number; taskId: number;
testData: string; testData: string;
type: ProblemType; type: ProblemType;
priority: number; priority: number;
param: StandardJudgeParameter | AnswerSubmissionJudgeParameter | InteractionJudgeParameter; param: StandardJudgeParameter | InteractionJudgeParameter;
}
export interface JudgeTask {
content: JudgeTaskContent;
extraData?: Buffer;
} }
export interface StandardJudgeParameter { export interface StandardJudgeParameter {
@ -24,10 +29,6 @@ export interface StandardJudgeParameter {
fileIOOutput?: string; fileIOOutput?: string;
} }
export interface AnswerSubmissionJudgeParameter {
answerFile: Buffer;
}
export interface InteractionJudgeParameter { export interface InteractionJudgeParameter {
timeLimit: number; timeLimit: number;
memoryLimit: number; memoryLimit: number;

38
src/daemon/judge/index.ts

@ -1,34 +1,17 @@
import { JudgeTask, ProblemType, TestData, StandardJudgeParameter } from '../interfaces'; import winston = require('winston');
import rmq = require('../rmq');
import { JudgeTaskContent, JudgeTask, ProblemType, TestData, StandardJudgeParameter } from '../interfaces';
import { StandardJudger } from './standard'; import { StandardJudger } from './standard';
import { JudgerBase } from './judger-base'; import { JudgerBase } from './judger-base';
import { JudgeResult, ErrorType, OverallResult, CompilationResult, TaskStatus, ProgressReportType } from '../../interfaces'; 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 { AnswerSubmissionJudger } from './submit-answer';
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, task: JudgeTaskContent,
extraData: Buffer,
reportProgress: (p: OverallResult) => Promise<void>, reportProgress: (p: OverallResult) => Promise<void>,
reportCompileProgress: (p: CompilationResult) => Promise<void> reportCompileProgress: (p: CompilationResult) => Promise<void>
): Promise<OverallResult> { ): Promise<OverallResult> {
@ -50,6 +33,8 @@ export async function judge(
let judger: JudgerBase; let judger: JudgerBase;
if (task.type === ProblemType.Standard) { if (task.type === ProblemType.Standard) {
judger = new StandardJudger(testData, task.param as StandardJudgeParameter, task.priority); judger = new StandardJudger(testData, task.param as StandardJudgeParameter, task.priority);
} else if (task.type === ProblemType.AnswerSubmission) {
judger = new AnswerSubmissionJudger(testData, extraData, task.priority);
} else { } else {
throw new Error(`Task type not supported`); throw new Error(`Task type not supported`);
} }
@ -74,6 +59,7 @@ export async function judge(
} }
winston.debug(`Judging...`); winston.debug(`Judging...`);
const judgeResult = await judger.judge(r => reportProgress({ compile: compileResult, judge: r })); const judgeResult = await judger.judge(r => reportProgress({ compile: compileResult, judge: r }));
await judger.cleanup();
return { compile: compileResult, judge: judgeResult }; return { compile: compileResult, judge: judgeResult };
} }

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

@ -26,7 +26,7 @@ export abstract class JudgerBase {
this.testData = t; this.testData = t;
} }
abstract preprocessTestData(): Promise<void>; async preprocessTestData(): Promise<void> { }
abstract compile(): Promise<CompilationResult>; abstract compile(): Promise<CompilationResult>;
@ -114,4 +114,6 @@ export abstract class JudgerBase {
return { subtasks: results }; return { subtasks: results };
} }
protected abstract judgeTestcase(curCase: TestcaseJudge, started: () => Promise<void>): Promise<TestcaseDetails>; protected abstract judgeTestcase(curCase: TestcaseJudge, started: () => Promise<void>): Promise<TestcaseDetails>;
async cleanup() { }
} }

101
src/daemon/judge/submit-answer.ts

@ -0,0 +1,101 @@
import winston = require('winston');
import pathLib = require('path');
import decompress = require('decompress');
import randomstring = require('randomstring');
import fse = require('fs-extra');
import { RPCTaskType, TestcaseResultType, TestcaseDetails, TaskStatus, CompilationResult, AnswerSubmissionRunTask, AnswerSubmissionRunResult } from '../../interfaces';
import { TestData, TestcaseJudge } from '../interfaces';
import { JudgerBase } from './judger-base';
import { compile } from './compile';
import { globalConfig as Cfg } from '../config';
import { runTask } from '../rmq';
import { readFileLength, readBufferLength } from '../../utils';
export class AnswerSubmissionJudger extends JudgerBase {
submissionContent: Buffer;
spjExecutableName: string = null;
tempDirectory: string;
constructor(testData: TestData, userSubmission: Buffer, priority: number) {
super(testData, priority);
winston.debug(`Submission size: ${userSubmission.length}`);
this.submissionContent = userSubmission;
this.tempDirectory = pathLib.join(Cfg.tempDirectory, 'SYZOJ-tmp-' + randomstring.generate(10));
}
async preprocessTestData(): Promise<void> {
if (this.testData.spj != null) {
winston.verbose("Compiling special judge.");
const [spjExecutableName, spjResult] = await compile(this.testData.spj.sourceCode,
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;
}
} else {
this.spjExecutableName = null;
}
}
async compile(): Promise<CompilationResult> {
await fse.mkdir(this.tempDirectory);
try {
await decompress(this.submissionContent, this.tempDirectory);
return { status: TaskStatus.Done };
} catch (err) {
return { status: TaskStatus.Failed, message: `Unable to decompress your answer` + err.toString() };
}
}
async judgeTestcase(curCase: TestcaseJudge, started: () => Promise<void>): Promise<TestcaseDetails> {
let userOutput: Buffer;
try {
userOutput = await fse.readFile(pathLib.join(this.tempDirectory, curCase.userOutputFile));
} catch (err) {
return {
type: TestcaseResultType.FileError,
time: NaN,
memory: NaN,
scoringRate: 0,
systemMessage: `Unable to open your answer: ${err.toString()}`
}
}
const task: AnswerSubmissionRunTask = {
testDataName: this.testData.name,
inputData: curCase.input,
answerData: curCase.output,
userAnswer: userOutput,
spjExecutableName: this.spjExecutableName
}
const [inputContent, outputContent, runResult]: [string, string, AnswerSubmissionRunResult] = await Promise.all([
readFileLength(pathLib.join(Cfg.testDataDirectory, this.testData.name, curCase.input), Cfg.dataDisplayLimit),
readFileLength(pathLib.join(Cfg.testDataDirectory, this.testData.name, curCase.output), Cfg.dataDisplayLimit),
runTask({ type: RPCTaskType.RunSubmitAnswer, task: task }, this.priority, started)
]) as any;
return {
type: runResult.result,
time: NaN,
memory: NaN,
input: { name: curCase.input, content: inputContent },
output: { name: curCase.output, content: outputContent },
scoringRate: runResult.scoringRate,
userOutput: readBufferLength(userOutput, Cfg.dataDisplayLimit),
spjMessage: runResult.spjMessage,
}
}
async cleanup(): Promise<void> {
await fse.remove(this.tempDirectory);
}
}

29
src/interfaces.ts

@ -21,13 +21,13 @@ export interface TestcaseDetails {
type: TestcaseResultType; type: TestcaseResultType;
time: number; time: number;
memory: number; memory: number;
input: FileContent; input?: FileContent;
output: FileContent; // Output in test data output?: FileContent; // Output in test data
scoringRate: number; // e.g. 0.5 scoringRate: number; // e.g. 0.5
userOutput: string; userOutput?: string;
userError: string; userError?: string;
spjMessage: string; spjMessage?: string;
systemMessage: string; systemMessage?: string;
}; };
export interface TestcaseResult { export interface TestcaseResult {
@ -69,7 +69,7 @@ export interface StandardRunResult {
userError: string; userError: string;
scoringRate: number; scoringRate: number;
spjMessage: string; spjMessage: string;
systemMessage: string; systemMessage?: string;
result: TestcaseResultType; result: TestcaseResultType;
} }
@ -85,6 +85,20 @@ export interface StandardRunTask {
spjExecutableName?: string; spjExecutableName?: string;
} }
export interface AnswerSubmissionRunTask {
testDataName: string;
inputData: string;
answerData: string;
userAnswer: Buffer;
spjExecutableName?: string;
}
export interface AnswerSubmissionRunResult {
scoringRate: number;
spjMessage: string;
result: TestcaseResultType;
}
export enum TaskStatus { export enum TaskStatus {
Waiting = 0, Waiting = 0,
Running = 1, Running = 1,
@ -122,6 +136,7 @@ export enum ProgressReportType {
Compiled = 2, Compiled = 2,
Progress = 3, Progress = 3,
Finished = 4, Finished = 4,
Reported = 5,
} }
export interface ProgressReportData { export interface ProgressReportData {

9
src/runner/index.ts

@ -6,7 +6,7 @@ import util = require('util');
import rmq = require('./rmq'); import rmq = require('./rmq');
import { RPCRequest, RPCTaskType } from '../interfaces'; import { RPCRequest, RPCTaskType } from '../interfaces';
import { compile } from './compile'; import { compile } from './compile';
import { judgeStandard } from './judge'; import { judgeStandard, judgeAnswerSubmission } from './judge';
(async function () { (async function () {
winston.info("Runner starts."); winston.info("Runner starts.");
@ -18,11 +18,12 @@ import { judgeStandard } from './judge';
winston.debug("Task type is compile"); winston.debug("Task type is compile");
return await compile(task.task); return await compile(task.task);
} else if (task.type === RPCTaskType.RunStandard) { } else if (task.type === RPCTaskType.RunStandard) {
winston.debug("Task type is judge standard");
return await judgeStandard(task.task); return await judgeStandard(task.task);
} else if (task.type === RPCTaskType.RunSubmitAnswer) {
return await judgeAnswerSubmission(task.task);
} else { } else {
winston.debug("Task type unsupported"); winston.warn("Task type unsupported");
throw new Error(`Task type ${task.type} not supported!`); throw new Error(`Task type ${task.type} not supported!`);
} }
}); });
})().then(() => { winston.info("Initialization logic completed."); }, (err) => { winston.error(util.inspect(err)); process.exit(1); }); })().then(() => { winston.info("Initialization logic completed."); }, (err) => { winston.error(util.inspect(err)); process.exit(1); });

54
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 { TestcaseResultType, StandardRunTask, StandardRunResult } from '../interfaces'; import { TestcaseResultType, StandardRunTask, StandardRunResult, AnswerSubmissionRunTask, AnswerSubmissionRunResult } 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';
@ -44,7 +44,7 @@ async function runSpj(spjBinDir: string, spjLanguage: Language): Promise<SpjResu
} else { } else {
const scoreString = await tryReadFile(pathLib.join(spjWorkingDir, scoreFileName)), const scoreString = await tryReadFile(pathLib.join(spjWorkingDir, scoreFileName)),
score = Number(scoreString); score = Number(scoreString);
const messageString = await readFileLength(pathLib.join(spjWorkingDir + messageFileName), Cfg.stderrDisplayLimit); const messageString = await readFileLength(pathLib.join(spjWorkingDir, messageFileName), Cfg.stderrDisplayLimit);
if ((!scoreString) || score === NaN || score < 0 || score > spjFullScore) { if ((!scoreString) || score === NaN || score < 0 || score > spjFullScore) {
return { return {
@ -74,7 +74,55 @@ async function runSpj(spjBinDir: string, spjLanguage: Language): Promise<SpjResu
} }
} }
export async function judgeStandard(task: StandardRunTask): Promise<StandardRunResult> { export async function judgeAnswerSubmission(task: AnswerSubmissionRunTask)
: Promise<AnswerSubmissionRunResult> {
try {
await createOrEmptyDir(spjWorkingDir);
const testDataPath = pathLib.join(Cfg.testDataDirectory, task.testDataName);
const inputFilePath = task.inputData != null ?
pathLib.join(testDataPath, task.inputData) : null;
if (inputFilePath != null)
await fse.copy(inputFilePath, pathLib.join(spjWorkingDir, 'input'));
const answerFilePath = task.answerData != null ?
pathLib.join(testDataPath, task.answerData) : null;
if (answerFilePath != null)
await fse.copy(answerFilePath, pathLib.join(spjWorkingDir, 'answer'));
await fse.writeFile(pathLib.join(spjWorkingDir, "user_out"), task.userAnswer);
if (task.spjExecutableName != null) {
const [spjBinDir, spjLanguage] = await fetchBinary(task.spjExecutableName);
winston.debug(`Using spj, language: ${spjLanguage.name}`);
if (inputFilePath != null)
await fse.copy(inputFilePath, pathLib.join(spjWorkingDir, 'input'));
winston.debug(`Running spj`);
const spjResult = await runSpj(spjBinDir, spjLanguage);
winston.debug('Judgement done!!');
return {
result: spjResult.status,
scoringRate: spjResult.score,
spjMessage: spjResult.message,
};
} else {
winston.debug(`Running diff`);
const diffResult = await runDiff(spjWorkingDir, 'user_out', 'answer');
winston.debug('Judgement done!!');
return {
result: diffResult.pass ? TestcaseResultType.Accepted : TestcaseResultType.WrongAnswer,
scoringRate: diffResult.pass ? 1 : 0,
spjMessage: diffResult.message,
};
}
} finally {
await tryEmptyDir(spjWorkingDir);
}
}
export async function judgeStandard(task: StandardRunTask)
: Promise<StandardRunResult> {
winston.debug("Standard judge task...", task); winston.debug("Standard judge task...", task);
try { try {
const testDataPath = pathLib.join(Cfg.testDataDirectory, task.testDataName); const testDataPath = pathLib.join(Cfg.testDataDirectory, task.testDataName);

11
src/utils.ts

@ -42,6 +42,15 @@ export async function tryReadFile(path: string, encoding = 'utf8'): Promise<stri
return fileContent; return fileContent;
} }
export function readBufferLength(buf: Buffer, lengthLimit: number, appendPrompt = fileTooLongPrompt)
: string {
let content = buf.toString('utf8', 0, lengthLimit);
if (buf.length > lengthLimit) {
content += '\n' + appendPrompt(buf.length, lengthLimit);
}
return content;
}
export async function readFileLength(path: string, lengthLimit: number, appendPrompt = fileTooLongPrompt) export async function readFileLength(path: string, lengthLimit: number, appendPrompt = fileTooLongPrompt)
: Promise<string> { : Promise<string> {
let file = -1; let file = -1;
@ -56,7 +65,7 @@ export async function readFileLength(path: string, lengthLimit: number, appendPr
} }
return ret; return ret;
} catch (e) { } catch (e) {
return ""; return null;
} finally { } finally {
if (file != -1) { if (file != -1) {
await fse.close(file); await fse.close(file);

Loading…
Cancel
Save