Coverage for control/api.py : 23%

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.
3* Country selection
4* Grouping by categories
5* Statistics
6"""
8from flask import make_response
10from config import Names as N
11from control.utils import mjson, mktsv, pick as G, serverprint
12from control.table import Table, SENSITIVE_TABLES, SENSITIVE_FIELDS
13from control.typ.related import castObjectId
16REVIEWED1 = "reviewed1"
17REVIEWED2 = "reviewed2"
18R1RANK = "r1Rank"
19R2RANK = "r2Rank"
21ASSESSED_STATUS = {
22 None: ("no assessment", "a-none"),
23 N.incomplete: ("started", "a-started"),
24 N.incompleteRevised: ("revision", "a-started"),
25 N.incompleteWithdrawn: ("withdrawn", "a-none"),
26 N.complete: ("filled-in", "a-self"),
27 N.completeRevised: ("revised", "a-self"),
28 N.completeWithdrawn: ("withdrawn", "a-none"),
29 N.submitted: ("in review", "a-inreview"),
30 N.submittedRevised: ("in review", "a-inreview"),
31 N.reviewReject: ("rejected", "a-rejected"),
32 N.reviewAccept: ("accepted", "a-accepted"),
33}
34ASSESSED_LABELS = {stage: info[0] for (stage, info) in ASSESSED_STATUS.items()}
35ASSESSED_CLASS = {stage: info[1] for (stage, info) in ASSESSED_STATUS.items()}
36ASSESSED_CLASS1 = {info[0]: info[1] for info in ASSESSED_STATUS.values()}
37ASSESSED_DEFAULT_CLASS = ASSESSED_STATUS[None][1]
38ASSESSED_RANK = {stage: i for (i, stage) in enumerate(ASSESSED_STATUS)}
40NO_REVIEW = {
41 N.incomplete,
42 N.incompleteRevised,
43 N.incompleteWithdrawn,
44 N.complete,
45 N.completeRevised,
46 N.completeWithdrawn,
47}
48IN_REVIEW = {
49 N.submitted,
50 N.submittedRevised,
51}
52ADVISORY_REVIEW = {
53 N.reviewAdviseAccept,
54 N.reviewAdviseReject,
55 N.reviewAdviseRevise,
56}
57FINAL_REVIEW = {
58 N.reviewAccept,
59 N.reviewReject,
60 N.reviewRevise,
61}
64REVIEWED_STATUS = {
65 None: ("", "r-none"),
66 "noReview": ("not reviewable", "r-noreview"),
67 "inReview": ("in review", "r-inreview"),
68 "skipReview": ("review skipped", "r-skipreview"),
69 N.reviewAdviseReject: ("rejected", "r-rejected"),
70 N.reviewAdviseAccept: ("accepted", "r-accepted"),
71 N.reviewAdviseRevise: ("revise", "r-revised"),
72 N.reviewReject: ("rejected", "r-rejected"),
73 N.reviewAccept: ("accepted", "r-accepted"),
74 N.reviewRevise: ("revise", "r-revised"),
75}
76REVIEW_LABELS = {stage: info[0] for (stage, info) in REVIEWED_STATUS.items()}
77REVIEW_CLASS = {stage: info[1] for (stage, info) in REVIEWED_STATUS.items()}
78REVIEW_CLASS1 = {info[0]: info[1] for info in REVIEWED_STATUS.values()}
79REVIEW_DEFAULT_CLASS = REVIEWED_STATUS[None][1]
80REVIEW_RANK = {stage: i for (i, stage) in enumerate(REVIEWED_STATUS)}
83class Api:
84 def __init__(self, context):
85 self.context = context
87 types = context.types
88 self.countryType = types.country
89 self.yearType = types.year
90 self.typeType = types.typeContribution
91 self.headers = dict(
92 json={
93 "Expires": "0",
94 "Cache-Control": "no-cache, no-store, must-revalidate",
95 "Content-Type": "application/json",
96 "Content-Encoding": "utf-8",
97 },
98 tsv={
99 "Expires": "0",
100 "Cache-Control": "no-cache, no-store, must-revalidate",
101 "Content-Type": "text/tab-separated-values",
102 "Content-Encoding": "utf-8",
103 },
104 )
106 def notimplemented(self, verb):
107 serverprint(f"Invalid api call requested: {verb}")
108 return make_response(mjson(None), self.headers["json"])
110 def list(self, givenTable):
111 parts = givenTable.rsplit(".", 1)
112 if len(parts) == 1:
113 table = givenTable
114 ext = "json"
115 else:
116 (table, ext) = parts
117 if ext not in {"json", "tsv"}:
118 serverprint(f"Invalid extension: {ext} in {givenTable}")
119 return make_response(mjson(None), self.headers["json"])
121 data = None
122 if table is not None and table not in SENSITIVE_TABLES:
123 if table == "contrib":
124 data = self.getContribs(ext)
125 else:
126 context = self.context
127 tableObj = Table(context, table)
128 data = tableObj.wrap(None, logical=True)
129 if data is None:
130 serverprint(f"Non existing table requested: {table}")
131 return make_response(
132 mjson(data) if ext == "json" else mktsv(data), self.headers[ext]
133 )
135 def view(self, table, givenEid):
136 record = None
137 eid = castObjectId(givenEid)
138 if table is not None and eid is not None and table not in SENSITIVE_TABLES:
139 context = self.context
140 tableObj = Table(context, table)
141 recordObj = tableObj.record(eid=eid)
142 record = recordObj.wrapLogical()
143 if table == "contrib":
144 extra = self.getExtra(record)
145 for (k, v) in extra.items():
146 record[k] = v
147 for k in SENSITIVE_FIELDS:
148 if k in record:
149 del record[k]
150 k = "typeContribution"
151 if k in record:
152 record["type"] = record[k]
153 del record[k]
154 if record is None:
155 serverprint(f"Non existing record requested: {table}/{givenEid}")
156 return make_response(mjson(record), self.headers["json"])
158 def getExtra(self, record):
159 context = self.context
160 wf = context.wf
161 workflow = wf.computeWorkflow(record)
163 selected = G(workflow, N.selected)
164 aStage = G(workflow, N.aStage)
165 r2Stage = G(workflow, N.r2Stage)
166 if r2Stage in {N.reviewAccept, N.reviewReject}:
167 aStage = r2Stage
168 score = G(workflow, N.score)
169 assessed = ASSESSED_STATUS[aStage][0]
170 aRank = (G(ASSESSED_RANK, aStage, default=0), score or 0)
171 if aStage != N.reviewAccept:
172 score = None
174 extra = {
175 N.assessed: assessed,
176 N.arank: aRank,
177 N.astage: aStage,
178 N.score: score,
179 N.selected: selected,
180 }
181 preR1Stage = G(workflow, N.r1Stage)
182 noReview = aStage is None or aStage in NO_REVIEW
183 inReview = aStage in IN_REVIEW
184 advReview = preR1Stage in ADVISORY_REVIEW
185 r1Stage = (
186 "noReview"
187 if noReview
188 else preR1Stage
189 if advReview
190 else "inReview"
191 if inReview
192 else "skipReview"
193 )
194 r2Stage = (
195 "noReview"
196 if noReview
197 else "inReview"
198 if inReview
199 else G(workflow, N.r2Stage)
200 )
201 reviewed1 = REVIEWED_STATUS[r1Stage][0]
202 reviewed2 = REVIEWED_STATUS[r2Stage][0]
203 r1Rank = G(REVIEW_RANK, r1Stage, default=0)
204 r2Rank = G(REVIEW_RANK, r2Stage, default=0)
205 extra.update(
206 {
207 REVIEWED1: reviewed1,
208 REVIEWED2: reviewed2,
209 R1RANK: r1Rank,
210 R2RANK: r2Rank,
211 N.r1Stage: r1Stage,
212 N.r2Stage: r2Stage,
213 }
214 )
215 return extra
217 def getContribs(self, ext):
218 context = self.context
219 db = context.db
220 countryType = self.countryType
221 yearType = self.yearType
222 typeType = self.typeType
224 asTsv = ext == "tsv"
226 contribs = []
228 if asTsv:
229 contribsFull = {G(r, N._id): r for r in db.getList(N.contrib)}
230 context = self.context
231 tableObj = Table(context, N.contrib)
233 for record in db.bulkContribWorkflow(None, False):
234 title = G(record, N.title)
235 contribId = G(record, N._id)
237 selected = G(record, N.selected)
238 aStage = G(record, N.aStage)
239 r2Stage = G(record, N.r2Stage)
240 if r2Stage in {N.reviewAccept, N.reviewReject}:
241 aStage = r2Stage
242 score = G(record, N.score)
243 assessed = ASSESSED_STATUS[aStage][0]
244 aRank = (G(ASSESSED_RANK, aStage, default=0), score or 0)
245 if aStage != N.reviewAccept:
246 score = None
248 countryRep = countryType.titleStr(
249 G(db.country, G(record, N.country)), markup=None
250 )
251 yearRep = yearType.titleStr(G(db.year, G(record, N.year)), markup=None)
252 typeRep = typeType.titleStr(
253 G(db.typeContribution, G(record, N.type)), markup=None
254 )
256 contribRecord = {
257 N._id: contribId,
258 N.country: countryRep,
259 N.year: yearRep,
260 N.type: typeRep,
261 N.title: title,
262 N.assessed: assessed,
263 N.arank: aRank,
264 N.astage: aStage,
265 N.score: score,
266 N.selected: selected,
267 }
268 preR1Stage = G(record, N.r1Stage)
269 noReview = aStage is None or aStage in NO_REVIEW
270 inReview = aStage in IN_REVIEW
271 advReview = preR1Stage in ADVISORY_REVIEW
272 r1Stage = (
273 "noReview"
274 if noReview
275 else preR1Stage
276 if advReview
277 else "inReview"
278 if inReview
279 else "skipReview"
280 )
281 r2Stage = (
282 "noReview"
283 if noReview
284 else "inReview"
285 if inReview
286 else G(record, N.r2Stage)
287 )
288 reviewed1 = REVIEWED_STATUS[r1Stage][0]
289 reviewed2 = REVIEWED_STATUS[r2Stage][0]
290 r1Rank = G(REVIEW_RANK, r1Stage, default=0)
291 r2Rank = G(REVIEW_RANK, r2Stage, default=0)
292 contribRecord.update(
293 {
294 REVIEWED1: reviewed1,
295 REVIEWED2: reviewed2,
296 R1RANK: r1Rank,
297 R2RANK: r2Rank,
298 N.r1Stage: r1Stage,
299 N.r2Stage: r2Stage,
300 }
301 )
302 if asTsv:
303 fullObj = tableObj.record(record=G(contribsFull, contribId, {}))
304 full = fullObj.wrapLogical()
305 contribRecord.update({
306 N.dateDecided: G(full, N.dateDecided),
307 N.vcc: G(full, N.vcc),
308 N.description: G(full, N.description),
309 N.contactPersonName: G(full, N.contactPersonName),
310 N.contactPersonEmail: G(full, N.contactPersonEmail),
311 N.urlContribution: G(full, N.urlContribution),
312 N.urlAcademic: G(full, N.urlAcademic),
313 N.tadirahObject: G(full, N.tadirahObject),
314 N.tadirahActivity: G(full, N.tadirahActivity),
315 N.tadirahTechnique: G(full, N.tadirahTechnique),
316 N.keyword: G(full, N.keyword),
317 N.discipline: G(full, N.discipline),
318 })
319 contribs.append(contribRecord)
321 return contribs