Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Overview page of contributions. 

2 

3* Country selection 

4* Grouping by categories 

5* Statistics 

6""" 

7 

8import json 

9from flask import request, make_response 

10 

11from config import Config as C, Names as N 

12from control.utils import pick as G, E, NBSP, COMMA, PLUS, MIN, ONE, MINONE, S, NL, TAB 

13from control.html import HtmlElements as H 

14 

15 

16CT = C.tables 

17CW = C.web 

18 

19URLS = CW.urls 

20PAGE = URLS[N.info][N.url] 

21PAGEX = f"""{PAGE}.tsv""" 

22 

23COL_SINGULAR = dict( 

24 country=N.country, 

25 assessed="stage", 

26 selected="stage", 

27 reviewed1="stage", 

28 reviewed2="stage", 

29 reviewer1="expert reviewer", 

30 reviewer2="final reviewer", 

31) 

32 

33COL_PLURAL = dict( 

34 country=N.countries, 

35 assessed="stages", 

36 selected="stages", 

37 reviewed1="stages", 

38 reviewed2="stages", 

39 reviewer1="expert reviewers", 

40 reviewer2="final reviewers", 

41) 

42 

43REVIEWER1 = "reviewer1" 

44REVIEWER2 = "reviewer2" 

45REVIEWED1 = "reviewed1" 

46REVIEWED2 = "reviewed2" 

47R1RANK = "r1Rank" 

48R2RANK = "r2Rank" 

49 

50COLSPECS = ( 

51 (N.country, str), 

52 (N.year, int), 

53 (N.type, str), 

54 (N.cost, int, """cost (€)"""), 

55 (N.assessed, tuple), 

56 (N.selected, bool), 

57 (REVIEWER1, str), 

58 (REVIEWER2, str), 

59 (REVIEWED1, str), 

60 (REVIEWED2, str), 

61 (N.title, str), 

62) 

63 

64GROUP_COLS = f""" 

65 country 

66 year 

67 type 

68 assessed 

69 {REVIEWER1} 

70 {REVIEWER2} 

71 {REVIEWED1} 

72 {REVIEWED2} 

73 selected 

74""".strip().split() 

75 

76SUBHEAD_X_COLS = set( 

77 """ 

78 cost 

79 title 

80""".strip().split() 

81) 

82 

83ASSESSED_STATUS = { 

84 None: ("no assessment", "a-none"), 

85 N.incomplete: ("started", "a-started"), 

86 N.incompleteRevised: ("revision", "a-started"), 

87 N.incompleteWithdrawn: ("withdrawn", "a-none"), 

88 N.complete: ("filled-in", "a-self"), 

89 N.completeRevised: ("revised", "a-self"), 

90 N.completeWithdrawn: ("withdrawn", "a-none"), 

91 N.submitted: ("in review", "a-inreview"), 

92 N.submittedRevised: ("in review", "a-inreview"), 

93 N.reviewReject: ("rejected", "a-rejected"), 

94 N.reviewAccept: ("accepted", "a-accepted"), 

95} 

96ASSESSED_LABELS = {stage: info[0] for (stage, info) in ASSESSED_STATUS.items()} 

97ASSESSED_CLASS = {stage: info[1] for (stage, info) in ASSESSED_STATUS.items()} 

98ASSESSED_CLASS1 = {info[0]: info[1] for info in ASSESSED_STATUS.values()} 

99ASSESSED_DEFAULT_CLASS = ASSESSED_STATUS[None][1] 

100ASSESSED_RANK = {stage: i for (i, stage) in enumerate(ASSESSED_STATUS)} 

101 

102NO_REVIEW = { 

103 N.incomplete, 

104 N.incompleteRevised, 

105 N.incompleteWithdrawn, 

106 N.complete, 

107 N.completeRevised, 

108 N.completeWithdrawn, 

109} 

110IN_REVIEW = { 

111 N.submitted, 

112 N.submittedRevised, 

113} 

114ADVISORY_REVIEW = { 

115 N.reviewAdviseAccept, 

116 N.reviewAdviseReject, 

117 N.reviewAdviseRevise, 

118} 

119FINAL_REVIEW = { 

120 N.reviewAccept, 

121 N.reviewReject, 

122 N.reviewRevise, 

123} 

124 

125 

126REVIEWED_STATUS = { 

127 None: ("", "r-none"), 

128 "noReview": ("not reviewable", "r-noreview"), 

129 "inReview": ("in review", "r-inreview"), 

130 "skipReview": ("review skipped", "r-skipreview"), 

131 N.reviewAdviseReject: ("rejected", "r-rejected"), 

132 N.reviewAdviseAccept: ("accepted", "r-accepted"), 

133 N.reviewAdviseRevise: ("revise", "r-revised"), 

134 N.reviewReject: ("rejected", "r-rejected"), 

135 N.reviewAccept: ("accepted", "r-accepted"), 

136 N.reviewRevise: ("revise", "r-revised"), 

137} 

138REVIEW_LABELS = {stage: info[0] for (stage, info) in REVIEWED_STATUS.items()} 

139REVIEW_CLASS = {stage: info[1] for (stage, info) in REVIEWED_STATUS.items()} 

140REVIEW_CLASS1 = {info[0]: info[1] for info in REVIEWED_STATUS.values()} 

141REVIEW_DEFAULT_CLASS = REVIEWED_STATUS[None][1] 

142REVIEW_RANK = {stage: i for (i, stage) in enumerate(REVIEWED_STATUS)} 

143 

144ALL = """All countries""" 

145 

146 

147class Overview: 

148 def __init__(self, context): 

149 self.context = context 

150 

151 types = context.types 

152 self.bool3Obj = types.bool3 

153 self.countryType = types.country 

154 self.yearType = types.year 

155 self.typeType = types.typeContribution 

156 self.userType = types.user 

157 

158 def getCountry(self, country): 

159 context = self.context 

160 db = context.db 

161 auth = context.auth 

162 user = auth.user 

163 countryType = self.countryType 

164 

165 self.userGroup = auth.groupRep() 

166 self.myCountry = auth.countryRep() 

167 

168 userCountryId = G(user, N.country) 

169 chosenCountry = None 

170 chosenCountryIso = None 

171 chosenCountryId = None 

172 

173 countryId = G(db.countryInv, country) if country else userCountryId 

174 

175 if countryId is not None: 

176 chosenCountryId = countryId 

177 countryInfo = G(db.country, chosenCountryId, default={}) 

178 chosenCountry = countryType.titleStr(countryInfo) 

179 chosenCountryIso = G(countryInfo, N.iso) 

180 

181 self.chosenCountry = chosenCountry 

182 self.chosenCountryId = chosenCountryId 

183 self.chosenCountryIso = chosenCountryIso 

184 

185 def getContribs(self, bulk): 

186 context = self.context 

187 db = context.db 

188 chosenCountryId = self.chosenCountryId 

189 countryType = self.countryType 

190 userType = self.userType 

191 yearType = self.yearType 

192 typeType = self.typeType 

193 isSuperUser = self.isSuperUser 

194 

195 users = db.user 

196 

197 contribs = {} 

198 for record in db.bulkContribWorkflow(chosenCountryId, bulk): 

199 title = G(record, N.title) 

200 contribId = G(record, N._id) 

201 

202 selected = G(record, N.selected) 

203 aStage = G(record, N.aStage) 

204 r2Stage = G(record, N.r2Stage) 

205 if r2Stage in {N.reviewAccept, N.reviewReject}: 205 ↛ 206line 205 didn't jump to line 206, because the condition on line 205 was never true

206 aStage = r2Stage 

207 score = G(record, N.score) 

208 assessed = ASSESSED_STATUS[aStage][0] 

209 aRank = (G(ASSESSED_RANK, aStage, default=0), score or 0) 

210 if aStage != N.reviewAccept: 210 ↛ 213line 210 didn't jump to line 213, because the condition on line 210 was never false

211 score = None 

212 

213 countryRep = countryType.titleStr(G(db.country, G(record, N.country))) 

214 yearRep = yearType.titleStr(G(db.year, G(record, N.year))) 

215 typeRep = typeType.titleStr(G(db.typeContribution, G(record, N.type))) 

216 cost = G(record, N.cost) 

217 

218 contribRecord = { 

219 N._id: contribId, 

220 N._cn: countryRep, 

221 N.country: countryRep, 

222 N.year: yearRep, 

223 N.type: typeRep, 

224 N.title: title, 

225 N.cost: cost, 

226 N.assessed: assessed, 

227 N.arank: aRank, 

228 N.astage: aStage, 

229 N.score: score, 

230 N.selected: selected, 

231 } 

232 if isSuperUser: 232 ↛ 233line 232 didn't jump to line 233, because the condition on line 232 was never true

233 preR1Stage = G(record, N.r1Stage) 

234 noReview = aStage is None or aStage in NO_REVIEW 

235 inReview = aStage in IN_REVIEW 

236 advReview = preR1Stage in ADVISORY_REVIEW 

237 r1Stage = ( 

238 "noReview" 

239 if noReview 

240 else preR1Stage if advReview 

241 else "inReview" 

242 if inReview 

243 else "skipReview" 

244 ) 

245 r2Stage = ( 

246 "noReview" 

247 if noReview 

248 else "inReview" 

249 if inReview 

250 else G(record, N.r2Stage) 

251 ) 

252 reviewed1 = REVIEWED_STATUS[r1Stage][0] 

253 reviewed2 = REVIEWED_STATUS[r2Stage][0] 

254 r1Rank = G(REVIEW_RANK, r1Stage, default=0) 

255 r2Rank = G(REVIEW_RANK, r2Stage, default=0) 

256 reviewer = {} 

257 for kind in ("E", "F"): 

258 reviewerId = G(record, getattr(N, f"reviewer{kind}")) 

259 if reviewerId is None: 

260 reviewer[kind] = None 

261 else: 

262 reviewerRecord = G(users, reviewerId) 

263 reviewerRep = userType.titleStr(reviewerRecord) 

264 reviewer[kind] = reviewerRep 

265 contribRecord.update( 

266 { 

267 REVIEWER1: reviewer["E"], 

268 REVIEWER2: reviewer["F"], 

269 REVIEWED1: reviewed1, 

270 REVIEWED2: reviewed2, 

271 R1RANK: r1Rank, 

272 R2RANK: r2Rank, 

273 N.r1Stage: r1Stage, 

274 N.r2Stage: r2Stage, 

275 } 

276 ) 

277 contribs[contribId] = contribRecord 

278 

279 self.contribs = contribs 

280 

281 def roTri(self, tri): 

282 return self.bool3Obj.toDisplay(tri, markup=False) 

283 

284 def wrap(self, asTsv=False): 

285 context = self.context 

286 db = context.db 

287 auth = context.auth 

288 countryType = self.countryType 

289 

290 isSuperUser = auth.superuser() 

291 self.isSuperUser = isSuperUser 

292 self.isCoord = auth.coordinator() 

293 

294 colSpecs = COLSPECS 

295 hiddenCols = ( 

296 set() if isSuperUser else {REVIEWER1, REVIEWER2, REVIEWED1, REVIEWED2} 

297 ) 

298 groupCols = [gc for gc in GROUP_COLS if gc not in hiddenCols] 

299 allGroupSet = set(groupCols) 

300 

301 accessRep = auth.credentials()[1] 

302 

303 rawBulk = request.args.get(N.bulk, E) 

304 bulk = True if rawBulk else False 

305 rawSortCol = request.args.get(N.sortcol, E) 

306 rawReverse = request.args.get(N.reverse, E) 

307 country = request.args.get(N.country, E) 

308 groups = COMMA.join( 

309 g for g in request.args.get(N.groups, E).split(COMMA) if g in allGroupSet 

310 ) 

311 

312 self.getCountry(country) 

313 self.getContribs(bulk) 

314 

315 chosenCountry = self.chosenCountry 

316 chosenCountryId = self.chosenCountryId 

317 chosenCountryIso = self.chosenCountryIso 

318 

319 if chosenCountryId is not None: 

320 colSpecs = [x for x in COLSPECS if x[0] != N.country] 

321 groups = self.rmGroup(groups.split(COMMA), N.country) 

322 groupCols = [x for x in groupCols if x != N.country] 

323 

324 cols = [c[0] for c in colSpecs] 

325 colSet = {c[0] for c in colSpecs} 

326 

327 self.types = dict((c[0], c[1]) for c in colSpecs) 

328 labels = dict((c[0], c[2] if len(c) > 2 else c[0]) for c in colSpecs) 

329 sortDefault = cols[-1] 

330 

331 groupsChosen = [] if not groups else groups.split(COMMA) 

332 groupSet = set(groupsChosen) 

333 groupStr = ("""-by-""" if groupSet else E) + MIN.join(sorted(groupSet)) 

334 

335 sortCol = sortDefault if rawSortCol not in colSet else rawSortCol 

336 reverse = False if rawReverse not in {MINONE, ONE} else rawReverse == MINONE 

337 

338 self.cols = cols 

339 self.labels = labels 

340 self.bulk = bulk 

341 self.groupCols = groupCols 

342 self.sortCol = sortCol 

343 self.reverse = reverse 

344 

345 material = [] 

346 if not asTsv: 

347 material.append(H.h(3, """Country selection""")) 

348 

349 countryItems = [ 

350 H.a( 

351 ALL, 

352 ( 

353 f"""{PAGE}?bulk={rawBulk}&country=x&sortcol={rawSortCol}&""" 

354 f"""reverse={rawReverse}&groups={groups}""" 

355 ), 

356 cls="c-control", 

357 ) 

358 if chosenCountryId 

359 else H.span(ALL, cls="c-focus") 

360 ] 

361 for (cid, countryInfo) in db.country.items(): 

362 if not G(countryInfo, N.isMember): 

363 continue 

364 name = countryType.titleStr(countryInfo) 

365 iso = G(countryInfo, N.iso) 

366 

367 countryItems.append( 

368 H.span(name, cls="c-focus") 

369 if cid == chosenCountryId 

370 else H.a( 

371 name, 

372 ( 

373 f"""{PAGE}?bulk={rawBulk}&country={iso}&""" 

374 f"""sortcol={rawSortCol}&""" 

375 f"""reverse={rawReverse}&groups={groups}""" 

376 ), 

377 cls="c-control", 

378 ) 

379 ) 

380 material.append(H.p(countryItems, cls=N.countries)) 

381 

382 groupsAvailable = sorted(allGroupSet - set(groupsChosen)) 

383 groupOrder = groupsChosen + [g for g in cols if g not in groupSet] 

384 

385 if not asTsv: 

386 urlArgsBare = ( 

387 f"""country={chosenCountryIso or 'x'}&""" 

388 f"""sortcol={rawSortCol}&reverse={rawReverse}""" 

389 ) 

390 urlArgs = f"""{urlArgsBare}&bulk={rawBulk}""" 

391 urlArgsBulk0 = f"""{urlArgsBare}&bulk=&groups={groups}""" 

392 urlArgsBulk1 = f"""{urlArgsBare}&bulk=1&groups={groups}""" 

393 urlStart1 = f"""{PAGE}?{urlArgs}""" 

394 urlStart = f"""{urlStart1}&groups=""" 

395 availableReps = E.join( 

396 H.a( 

397 f"""+{g}""", 

398 (f"""{urlStart}{self.addGroup(groupsChosen, g)}"""), 

399 cls="g-add", 

400 ) 

401 for g in groupsAvailable 

402 ) 

403 chosenReps = E.join( 

404 H.a( 

405 f"""-{g}""", 

406 f"""{urlStart}{self.rmGroup(groupsChosen, g)}""", 

407 cls="g-rm", 

408 ) 

409 for g in groupsChosen 

410 ) 

411 clearGroups = ( 

412 E 

413 if len(chosenReps) == 0 

414 else H.iconx( 

415 N.clear, urlStart1, cls="g-x", title="""clear all groups""" 

416 ) 

417 ) 

418 rArgs = f"""{urlArgs}&groups={groups}""" 

419 

420 headerLine = self.ourCountryHeaders( 

421 country, groups, asTsv, groupOrder=groupOrder, 

422 ) 

423 

424 contribs = self.contribs 

425 nContribs = len(contribs) 

426 plural = E if nContribs == 1 else S 

427 things = f"""{len(contribs)} contribution{plural}""" 

428 origin = chosenCountry or ALL.lower() 

429 

430 if not asTsv: 

431 bulkHead = "Show all" if bulk else "Show bulk imports only" 

432 bulkPre = H.span("Showing bulk imports only") + NBSP if bulk else E 

433 bulkUrl = f"""{PAGE}?{urlArgsBulk0 if bulk else urlArgsBulk1}""" 

434 material.append(H.h(3, """Grouping""")) 

435 material.append( 

436 H.table( 

437 [], 

438 [ 

439 ( 

440 [ 

441 ("""available groups""", dict(cls="mtl")), 

442 (availableReps, dict(cls="mtd")), 

443 (NBSP, {}), 

444 ], 

445 {}, 

446 ), 

447 ( 

448 [ 

449 ("""chosen groups""", dict(cls="mtl")), 

450 (chosenReps, dict(cls="mtd")), 

451 (clearGroups, {}), 

452 ], 

453 {}, 

454 ), 

455 ], 

456 cls="mt", 

457 ) 

458 ) 

459 material.append(H.p([bulkPre, H.a(bulkHead, bulkUrl, cls="button small")])) 

460 material.append( 

461 H.h( 

462 3, 

463 [ 

464 f"""{things} from {origin}""", 

465 H.a( 

466 """Download as Excel""", 

467 f"""{PAGEX}?{rArgs}""", 

468 target="_blank", 

469 cls="button large", 

470 ), 

471 ], 

472 ) 

473 ) 

474 

475 (thisMaterial, groupRel) = self.groupList( 

476 groupsChosen, chosenCountry, chosenCountry, asTsv, 

477 ) 

478 

479 if asTsv: 

480 material.append(thisMaterial) 

481 else: 

482 material.append(H.table([headerLine], thisMaterial, cls="cc")) 

483 material.append(groupRel) 

484 

485 if asTsv: 

486 countryRep = ( 

487 "all-countries" 

488 if country == "x" 

489 else country 

490 if country 

491 else "their-country" 

492 ) 

493 fileName = f"""dariah-{countryRep}{groupStr}-for-{accessRep}""" 

494 headers = { 

495 "Expires": "0", 

496 "Cache-Control": "no-cache, no-store, must-revalidate", 

497 "Content-Type": "text/csv", 

498 "Content-Disposition": f'attachment; filename="{fileName}"', 

499 "Content-Encoding": "identity", 

500 } 

501 tsv = f"""\ufeff{headerLine}\n{NL.join(material)}""".encode("""utf_16_le""") 

502 data = make_response(tsv, headers) 

503 else: 

504 data = E.join(material) 

505 

506 return data 

507 

508 def groupList( 

509 self, groups, selectedCountry, chosenCountry, asTsv, 

510 ): 

511 cols = self.cols 

512 groupCols = self.groupCols 

513 contribs = self.contribs 

514 sortCol = self.sortCol 

515 reverse = self.reverse 

516 

517 if len(groups) == 0: 517 ↛ 538line 517 didn't jump to line 538, because the condition on line 517 was never false

518 groupedList = sorted( 

519 contribs.values(), key=self.contribKey(sortCol), reverse=reverse 

520 ) 

521 if asTsv: 

522 return ( 

523 NL.join( 

524 self.formatContrib(contrib, None, chosenCountry, asTsv,) 

525 for contrib in groupedList 

526 ), 

527 E, 

528 ) 

529 else: 

530 return ( 

531 [ 

532 self.formatContrib(contrib, None, chosenCountry, asTsv,) 

533 for contrib in groupedList 

534 ], 

535 E, 

536 ) 

537 

538 preGroups = groups[0:-1] 

539 lastGroup = groups[-1] 

540 

541 groupLen = len(groups) 

542 groupSet = set(groups) 

543 groupOrder = groups + [g for g in cols if g not in groupSet] 

544 

545 groupedList = {} 

546 

547 for c in contribs.values(): 

548 dest = groupedList 

549 for g in preGroups: 

550 dest = dest.setdefault(G(c, g), {}) 

551 dest = dest.setdefault(G(c, lastGroup), []) 

552 dest.append(c) 

553 

554 material = [] 

555 maxGroupId = 1 

556 groupRel = {} 

557 

558 def groupMaterial(gList, depth, groupValues, parentGroupId): 

559 groupSet = set(groupValues.keys()) 

560 

561 nonlocal maxGroupId 

562 maxGroupId += 1 

563 thisGroupId = maxGroupId 

564 thisGroupId = COMMA.join(f"""{k}:{v}""" for (k, v) in groupValues.items()) 

565 groupRel.setdefault(str(parentGroupId), []).append(str(thisGroupId)) 

566 

567 headIndex = len(material) 

568 material.append(MIN if asTsv else ([(MIN, {})], {})) 

569 nRecords = 0 

570 nGroups = 0 

571 cost = 0 

572 if type(gList) is list: 

573 for rec in sorted( 

574 ( 

575 {k: v for (k, v) in list(d.items()) if k not in groupValues} 

576 for d in gList 

577 ), 

578 key=self.contribKey(sortCol), 

579 reverse=reverse, 

580 ): 

581 nRecords += 1 

582 nGroups += 1 

583 cost += G(rec, N.cost) or 0 

584 material.append( 

585 self.formatContrib( 

586 rec, 

587 thisGroupId, 

588 chosenCountry, 

589 asTsv, 

590 groupOrder=groupOrder, 

591 hide=True, 

592 ) 

593 ) 

594 else: 

595 newGroup = groups[depth] 

596 for groupValue in sorted( 

597 gList.keys(), 

598 key=self.contribKey(newGroup, individual=True), 

599 reverse=reverse, 

600 ): 

601 nGroups += 1 

602 newGroupValues = {} 

603 newGroupValues.update(groupValues) 

604 newGroupValues[newGroup] = groupValue 

605 (nRecordsG, costG) = groupMaterial( 

606 gList[groupValue], depth + 1, newGroupValues, thisGroupId, 

607 ) 

608 nRecords += nRecordsG 

609 cost += costG 

610 groupValuesT = {} 

611 if depth > 0: 

612 thisGroup = groups[depth - 1] 

613 groupValuesT[thisGroup] = groupValues[thisGroup] 

614 # groupValuesT.update(groupValues) 

615 groupValuesT[N.cost] = cost 

616 groupValuesT[N.title] = self.colRep(N.contribution, nRecords) 

617 groupValuesT[N._cn] = G(groupValues, N.country) 

618 if depth == 0: 

619 for g in groupCols + [N.title]: 

620 label = selectedCountry if g == N.country else N.all 

621 controls = ( 

622 self.expandAcontrols(g) if g in groups or g == N.title else E 

623 ) 

624 groupValuesT[g] = label if asTsv else f"""{label} {controls}""" 

625 material[headIndex] = self.formatContrib( 

626 groupValuesT, 

627 parentGroupId, 

628 chosenCountry, 

629 asTsv, 

630 groupOrder=groupOrder, 

631 groupSet=groupSet, 

632 subHead=True, 

633 allHead=depth == 0, 

634 groupLen=groupLen, 

635 depth=depth, 

636 thisGroupId=thisGroupId, 

637 nGroups=nGroups, 

638 ) 

639 return (nRecords, cost) 

640 

641 groupMaterial(groupedList, 0, {}, 1) 

642 return ( 

643 NL.join(material) if asTsv else material, 

644 H.script(f"""var groupRel = {json.dumps(groupRel)}"""), 

645 ) 

646 

647 def formatContrib( 

648 self, 

649 contrib, 

650 groupId, 

651 chosenCountry, 

652 asTsv, 

653 groupOrder=None, 

654 groupSet=set(), 

655 subHead=False, 

656 allHead=False, 

657 groupLen=None, 

658 depth=None, 

659 thisGroupId=None, 

660 nGroups=None, 

661 hide=False, 

662 ): 

663 cols = self.cols 

664 isSuperUser = self.isSuperUser 

665 

666 if groupOrder is None: 666 ↛ 668line 666 didn't jump to line 668, because the condition on line 666 was never false

667 groupOrder = cols 

668 contribId = G(contrib, N._id) 

669 (assessedLabel, assessedClass) = self.wrapStatus(contrib, subHead=subHead) 

670 if allHead: 670 ↛ 671line 670 didn't jump to line 671, because the condition on line 670 was never true

671 selected = G(contrib, N.selected) or E 

672 if asTsv: 

673 selected = self.valTri(selected) 

674 assessedClass = E 

675 else: 

676 selected = G(contrib, N.selected) 

677 selected = ( 

678 (self.valTri(selected) if asTsv else self.roTri(selected)) 

679 if N.selected in contrib 

680 else E 

681 ) 

682 rawTitle = G(contrib, N.title) or E 

683 title = ( 

684 rawTitle 

685 if asTsv 

686 else rawTitle 

687 if subHead 

688 else H.a( 

689 f"""{rawTitle or "? missing title ?"}""", 

690 f"""/{N.contrib}/{N.item}/{contribId}""", 

691 ) 

692 if N.title in contrib 

693 else E 

694 ) 

695 

696 values = { 

697 N.country: G(contrib, N.country) or E, 

698 N.year: G(contrib, N.year) or E, 

699 N.type: G(contrib, N.type) or E, 

700 N.cost: self.euro(G(contrib, N.cost)), 

701 N.assessed: assessedLabel, 

702 N.selected: selected, 

703 N.title: title, 

704 } 

705 if isSuperUser: 705 ↛ 706line 705 didn't jump to line 706, because the condition on line 705 was never true

706 (r1Label, r1Class) = self.wrapStatus(contrib, subHead=subHead, kind="1") 

707 (r2Label, r2Class) = self.wrapStatus(contrib, subHead=subHead, kind="2") 

708 if allHead: 

709 r1Class = E 

710 r2Class = E 

711 reviewer1 = G(contrib, REVIEWER1) 

712 reviewer2 = G(contrib, REVIEWER2) 

713 values.update( 

714 { 

715 REVIEWER1: reviewer1 or E, 

716 REVIEWER2: reviewer2 or E, 

717 REVIEWED1: r1Label, 

718 REVIEWED2: r2Label, 

719 } 

720 ) 

721 recCountry = G(contrib, N._cn) or G(values, N.country) 

722 if depth is not None: 722 ↛ 723line 722 didn't jump to line 723, because the condition on line 722 was never true

723 xGroup = groupOrder[depth] if depth == 0 or depth < groupLen else N.title 

724 xName = N.contribution if xGroup == N.title else xGroup 

725 xRep = self.colRep(xName, nGroups) 

726 values[xGroup] = ( 

727 xRep 

728 if asTsv 

729 else ( 

730 f"""{self.expandControls(thisGroupId, True)} {xRep}""" 

731 if xGroup == N.title 

732 else f"""{values[xGroup]} ({xRep}) {self.expandControls(thisGroupId)}""" 

733 if depth > 0 

734 else f"""{values[xGroup]} ({xRep}) {self.expandControls(thisGroupId)}""" 

735 ) 

736 ) 

737 if not asTsv: 737 ↛ 743line 737 didn't jump to line 743, because the condition on line 737 was never false

738 classes = {col: f"c-{col}" for col in groupOrder} 

739 classes["assessed"] += f" {assessedClass}" 

740 if isSuperUser: 740 ↛ 741line 740 didn't jump to line 741, because the condition on line 740 was never true

741 classes[REVIEWED1] = r1Class 

742 classes[REVIEWED2] = r2Class 

743 if asTsv: 743 ↛ 744line 743 didn't jump to line 744, because the condition on line 743 was never true

744 columns = TAB.join( 

745 self.disclose(values, col, recCountry) or E for col in groupOrder 

746 ) 

747 else: 

748 columns = [ 

749 ( 

750 self.disclose(values, col, recCountry), 

751 dict( 

752 cls=( 

753 f"{classes[col]} " 

754 + self.subHeadClass(col, groupSet, subHead, allHead) 

755 ) 

756 ), 

757 ) 

758 for col in groupOrder 

759 ] 

760 if not asTsv: 760 ↛ 765line 760 didn't jump to line 765, because the condition on line 760 was never false

761 hideRep = " hide" if hide else E 

762 displayAtts = ( 

763 {} if groupId is None else dict(cls=f"dd{hideRep}", gid=groupId) 

764 ) 

765 return columns if asTsv else (columns, displayAtts) 

766 

767 def contribKey(self, col, individual=False): 

768 types = self.types 

769 

770 colType = types[col] 

771 

772 def makeKey(contrib): 

773 if col == N.assessed: 773 ↛ 774line 773 didn't jump to line 774, because the condition on line 773 was never true

774 return G(contrib, N.arank, default=(E, 0)) 

775 elif col == REVIEWED1: 775 ↛ 776line 775 didn't jump to line 776, because the condition on line 775 was never true

776 return G(contrib, R1RANK, default=0) 

777 elif col == REVIEWED2: 777 ↛ 778line 777 didn't jump to line 778, because the condition on line 777 was never true

778 return G(contrib, R2RANK, default=0) 

779 value = G(contrib, col) 

780 if value is None: 780 ↛ 781line 780 didn't jump to line 781, because the condition on line 780 was never true

781 return E if colType is str else 0 

782 if colType is str: 782 ↛ 784line 782 didn't jump to line 784, because the condition on line 782 was never false

783 return value.lower() 

784 if colType is bool: 

785 return 1 if value else -1 

786 return E 

787 

788 def makeKeyInd(value): 

789 if col == N.assessed: 

790 return value or E 

791 if value is None: 

792 return E if colType is str else 0 

793 if colType is str: 

794 return value.lower() 

795 if colType is bool: 

796 return 1 if value else -1 

797 return E 

798 

799 return makeKeyInd if individual else makeKey 

800 

801 def ourCountryHeaders(self, country, groups, asTsv, groupOrder=None): 

802 cols = self.cols 

803 labels = self.labels 

804 bulk = self.bulk 

805 sortCol = self.sortCol 

806 reverse = self.reverse 

807 

808 if groupOrder is None: 808 ↛ 809line 808 didn't jump to line 809, because the condition on line 808 was never true

809 groupOrder = cols 

810 

811 if asTsv: 

812 headers = E 

813 sep = E 

814 for col in groupOrder: 

815 label = labels[col] 

816 colControl = label 

817 headers += f"""{sep}{colControl}""" 

818 sep = TAB 

819 

820 else: 

821 headers = [] 

822 dirClass = N.desc if reverse else N.asc 

823 dirIcon = N.adown if reverse else N.aup 

824 rawBulk = "1" if bulk else E 

825 urlStart = f"""{PAGE}?bulk={rawBulk}&country={country}&groups={groups}""" 

826 for col in groupOrder: 

827 isSorted = col == sortCol 

828 thisClass = f"c-{col}" 

829 icon = E 

830 if isSorted: 

831 thisClass += f" {dirClass}" 

832 nextReverse = not reverse 

833 icon = H.iconx(dirIcon) 

834 else: 

835 nextReverse = False 

836 reverseRep = -1 if nextReverse else 1 

837 label = labels[col] 

838 sep = NBSP if icon else E 

839 colControl = H.a( 

840 f"""{label}{icon}""", 

841 f"""{urlStart}&sortcol={col}&reverse={reverseRep}""", 

842 ) 

843 headers.append((colControl, dict(cls=f"och {thisClass}"))) 

844 headers = (headers, {}) 

845 

846 return headers 

847 

848 def disclose(self, values, colName, recCountry): 

849 context = self.context 

850 auth = context.auth 

851 isSuperUser = self.isSuperUser 

852 isCoord = auth.coordinator(countryId=recCountry) 

853 

854 disclosed = ( 

855 (colName not in {N.cost, REVIEWER1, REVIEWED1, REVIEWER2, REVIEWED2}) 

856 or isSuperUser 

857 or isCoord 

858 ) 

859 value = values[colName] if disclosed else N.undisclosed 

860 return value 

861 

862 @staticmethod 

863 def wrapStatus(contrib, subHead=False, kind=None): 

864 aStage = G(contrib, N.astage) 

865 if kind is None: 865 ↛ 878line 865 didn't jump to line 878, because the condition on line 865 was never false

866 assessed = G(contrib, N.assessed) or E 

867 score = G(contrib, N.score) 

868 scoreRep = E if score is None else f"""{score}% - """ 

869 baseLabel = assessed if subHead else G(ASSESSED_LABELS, aStage, default=E) 

870 aClass = ( 

871 G(ASSESSED_CLASS1, assessed, default=ASSESSED_DEFAULT_CLASS) 

872 if subHead 

873 else G(ASSESSED_CLASS, aStage, default=ASSESSED_DEFAULT_CLASS) 

874 ) 

875 aLabel = baseLabel if subHead else f"""{scoreRep}{baseLabel}""" 

876 return (aLabel, aClass) 

877 else: 

878 rStage = G(contrib, N.r1Stage if kind == "1" else N.r2Stage) 

879 reviewed = G(contrib, REVIEWED1 if kind == "1" else REVIEWED2) or E 

880 rClass = ( 

881 G(REVIEW_CLASS1, reviewed, default=REVIEW_DEFAULT_CLASS) 

882 if subHead 

883 else G(REVIEW_CLASS, rStage, default=REVIEW_DEFAULT_CLASS) 

884 ) 

885 rLabel = reviewed if subHead else G(REVIEW_LABELS, rStage, default=E) 

886 return (rLabel, rClass) 

887 

888 @staticmethod 

889 def colRep(col, n): 

890 itemRep = ( 

891 G(COL_SINGULAR, col, default=col) 

892 if n == 1 

893 else G(COL_PLURAL, col, default=f"""{col}s""") 

894 ) 

895 return f"""{n} {itemRep}""" 

896 

897 @staticmethod 

898 def addGroup(groups, g): 

899 return COMMA.join(groups + [g]) 

900 

901 @staticmethod 

902 def rmGroup(groups, g): 

903 return COMMA.join(h for h in groups if h != g) 

904 

905 @staticmethod 

906 def expandControls(gid, hide=False): 

907 hideRep = " hide" if hide else E 

908 showRep = E if hide else " hide" 

909 return E.join( 

910 ( 

911 H.iconx(N.cdown, href=E, cls=f"""dc{showRep}""", gid=gid), 

912 H.iconx(N.cup, href=E, cls=f"""dc{hideRep}""", gid=gid), 

913 ) 

914 ) 

915 

916 @staticmethod 

917 def expandAcontrols(group): 

918 return E.join( 

919 ( 

920 H.iconx(N.addown, href=E, cls="dca", gn=group), 

921 H.iconx(N.adup, href=E, cls="dca", gn=group), 

922 ) 

923 ) 

924 

925 @staticmethod 

926 def euro(amount): 

927 return E if amount is None else f"""{int(round(amount)):,}""" 

928 

929 @staticmethod 

930 def valTri(tri): 

931 return E if tri is None else PLUS if tri else MIN 

932 

933 @staticmethod 

934 def subHeadClass(col, groupSet, subHead, allHead): 

935 theClass = ( 

936 "allhead" 

937 if allHead and col == N.selected 

938 else "subhead" 

939 if allHead or (subHead and (col in groupSet or col in SUBHEAD_X_COLS)) 

940 else E 

941 ) 

942 return f" {theClass}" if theClass else E