| 293 | } |
| 294 | |
| 295 | export class ProblemDetailHandler extends ContestDetailBaseHandler { |
| 296 | pdoc: ProblemDoc; |
| 297 | udoc: User; |
| 298 | psdoc: ProblemStatusDoc; |
| 299 | |
| 300 | @route('pid', Types.ProblemId, true) |
| 301 | @query('tid', Types.ObjectId, true) |
| 302 | async _prepare(domainId: string, pid: number | string, tid?: ObjectId) { |
| 303 | this.pdoc = await problem.get(domainId, pid); |
| 304 | if (!this.pdoc) throw new ProblemNotFoundError(domainId, pid); |
| 305 | if (tid) { |
| 306 | if (!this.tdoc?.pids?.includes(this.pdoc.docId)) throw new ContestNotFoundError(domainId, tid); |
| 307 | if (contest.isNotStarted(this.tdoc)) throw new ContestNotLiveError(tid); |
| 308 | if (!contest.isDone(this.tdoc, this.tsdoc) && (!this.tsdoc?.attend || !this.tsdoc.startAt)) throw new ContestNotAttendedError(tid); |
| 309 | // Delete problem-related info in contest mode |
| 310 | if (this.pdoc.tag) this.pdoc.tag.length = 0; |
| 311 | delete this.pdoc.nAccept; |
| 312 | delete this.pdoc.nSubmit; |
| 313 | delete this.pdoc.difficulty; |
| 314 | delete this.pdoc.stats; |
| 315 | } else if (!problem.canViewBy(this.pdoc, this.user)) { |
| 316 | throw new PermissionError(PERM.PERM_VIEW_PROBLEM_HIDDEN); |
| 317 | } |
| 318 | let ddoc = this.domain; |
| 319 | if (this.pdoc.reference) { |
| 320 | ddoc = await domain.get(this.pdoc.reference.domainId); |
| 321 | const pdoc = await problem.get(this.pdoc.reference.domainId, this.pdoc.reference.pid); |
| 322 | if (!ddoc || !pdoc) throw new ProblemNotFoundError(this.pdoc.reference.domainId, this.pdoc.reference.pid); |
| 323 | this.pdoc.config = pdoc.config; |
| 324 | this.pdoc.additional_file = pdoc.additional_file; |
| 325 | } |
| 326 | if (typeof this.pdoc.config !== 'string') { |
| 327 | let baseLangs; |
| 328 | const t = []; |
| 329 | if (this.pdoc.config.langs) t.push(this.pdoc.config.langs); |
| 330 | if (ddoc.langs) t.push(ddoc.langs.split(',').map((i) => i.trim()).filter((i) => i)); |
| 331 | if (this.domain.langs) t.push(this.domain.langs.split(',').map((i) => i.trim()).filter((i) => i)); |
| 332 | if (this.tdoc?.langs?.length) t.push(this.tdoc.langs); |
| 333 | if (this.pdoc.config.type === 'remote_judge') { |
| 334 | const p = this.pdoc.config.subType; |
| 335 | const dl = Object.keys(setting.langs).filter((i) => i.startsWith(`${p}.`) || setting.langs[i].validAs[p]); |
| 336 | if (setting.langs[p]) dl.push(p); |
| 337 | baseLangs = dl; |
| 338 | } else { |
| 339 | const needHiddenLangs = flattenDeep(t).length; |
| 340 | baseLangs = Object.keys(setting.langs).filter((i) => |
| 341 | (needHiddenLangs ? !setting.langs[i].remote : !setting.langs[i].remote && !setting.langs[i].hidden)); |
| 342 | } |
| 343 | this.pdoc.config.langs = ['objective', 'submit_answer'].includes(this.pdoc.config.type) ? ['_'] : intersection(baseLangs, ...t); |
| 344 | } |
| 345 | await this.ctx.parallel('problem/get', this.pdoc, this); |
| 346 | [this.psdoc, this.udoc] = await Promise.all([ |
| 347 | problem.getStatus(domainId, this.pdoc.docId, this.user._id), |
| 348 | user.getById(domainId, this.pdoc.owner), |
| 349 | ]); |
| 350 | const [scnt, dcnt] = await Promise.all([ |
| 351 | solution.count(domainId, { parentId: this.pdoc.docId }), |
| 352 | discussion.count(domainId, { parentId: this.pdoc.docId }), |