Skip to content

Commit e89d5b5

Browse files
authored
CLDR-18610 VettingParticipation separate E/M/P; Vetters only; further improvements (#4925)
1 parent 00efb69 commit e89d5b5

File tree

6 files changed

+100
-43
lines changed

6 files changed

+100
-43
lines changed

tools/cldr-apps/js/src/esm/cldrInfo.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import * as cldrStatus from "./cldrStatus.mjs";
1111
import * as cldrSurvey from "./cldrSurvey.mjs";
1212
import * as cldrTable from "./cldrTable.mjs";
1313
import * as cldrText from "./cldrText.mjs";
14-
import * as cldrUserLevels from "./cldrUserLevels.mjs";
1514
import * as cldrVote from "./cldrVote.mjs";
1615
import * as cldrVoteTable from "./cldrVoteTable.mjs";
1716
import * as cldrVue from "./cldrVue.mjs";

tools/cldr-apps/js/src/esm/cldrVettingParticipation.mjs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ const COLUMN_TITLE_COVERAGE_LEVEL = "Coverage";
3636
const COLUMN_TITLE_LEVEL = "Level";
3737
const COLUMN_TITLE_PROGRESS_PERCENT = "Done";
3838
const COLUMN_TITLE_ABSTAIN_COUNT = "Abst.";
39-
const COLUMN_TITLE_EMP = "EMP";
40-
const COLUMN_TITLE_MP = "MP";
39+
const COLUMN_TITLE_ERROR_COUNT = "Err.";
40+
const COLUMN_TITLE_MISSING_COUNT = "Miss.";
41+
const COLUMN_TITLE_PROVISIONAL_COUNT = "Prov.";
4142
const COLUMN_TITLE_USER_ID = "User#";
4243
const COLUMN_TITLE_USER_EMAIL = "Email";
4344
const COLUMN_TITLE_USER_NAME = "Name";
@@ -66,13 +67,18 @@ const COLUMNS = [
6667
default: 0,
6768
},
6869
{
69-
title: COLUMN_TITLE_EMP,
70-
comment: "Sum of errors + missing + provisional (for locale)",
70+
title: COLUMN_TITLE_ERROR_COUNT,
71+
comment: "Number of errors (for locale)",
7172
default: 0,
7273
},
7374
{
74-
title: COLUMN_TITLE_MP,
75-
comment: "Sum of missing + provisional (for locale)",
75+
title: COLUMN_TITLE_MISSING_COUNT,
76+
comment: "Number of missing paths (for locale)",
77+
default: 0,
78+
},
79+
{
80+
title: COLUMN_TITLE_PROVISIONAL_COUNT,
81+
comment: "Number of provisional paths (for locale)",
7682
default: 0,
7783
},
7884
{
@@ -161,6 +167,7 @@ function makeRequest(req) {
161167
"cldrVettingParticipation.makeRequest, fetching initial data"
162168
);
163169
}
170+
vpData.startTime = Date.now();
164171
const p = new URLSearchParams();
165172
p.append("what", "vetting_participation");
166173
p.append("s", cldrStatus.getSessionId());
@@ -187,6 +194,8 @@ function loadHandler(json) {
187194
console.dir({ json });
188195
cldrNotify.error("Error loading vetting participation", json.err);
189196
} else if (callbackToSetData) {
197+
vpData.firstResponseTime = Date.now();
198+
190199
// This json is the response to the initial request to what=vetting_participation
191200
storeInitialResponseData(json);
192201

@@ -268,10 +277,13 @@ function preloadVotingResults() {
268277
if (wasCancelled()) {
269278
return;
270279
}
271-
// "user" here is an object; id = user.id
272-
if (!isRegularVetter(user)) {
280+
if (!user.locales || !user.locales.length) {
281+
if (VP_DEBUG) {
282+
console.log("user.locales is missing or empty for user id " + id);
283+
}
273284
continue;
274285
}
286+
// "user" here is an object; id = user.id
275287
user.data = {};
276288
for (const locale of user.locales.sort()) {
277289
if (VP_DEBUG) {
@@ -322,6 +334,7 @@ function preloadVotingResults() {
322334

323335
async function createTable() {
324336
const columnIndex = getIndexOfColumnsByTitle();
337+
vpData.accountColumnIndex = columnIndex[COLUMN_TITLE_USER_ID];
325338
const rowMap = {};
326339
for (const [id, user] of Object.entries(vpData.uidToUser)) {
327340
if (VP_DEBUG) {
@@ -366,9 +379,9 @@ async function createTable() {
366379
daysAgo = "♾️";
367380
}
368381
row[columnIndex[COLUMN_TITLE_LAST_MOD]] = daysAgo;
369-
row[columnIndex[COLUMN_TITLE_EMP]] =
370-
errorCount + missingCount + provisionalCount;
371-
row[columnIndex[COLUMN_TITLE_MP]] = missingCount + provisionalCount;
382+
row[columnIndex[COLUMN_TITLE_ERROR_COUNT]] = errorCount;
383+
row[columnIndex[COLUMN_TITLE_MISSING_COUNT]] = missingCount;
384+
row[columnIndex[COLUMN_TITLE_PROVISIONAL_COUNT]] = provisionalCount;
372385
const sortKey = localeName + " " + user.org + " " + id;
373386
rowMap[sortKey] = [...row]; // clone the array since table will retain a reference
374387
}
@@ -387,7 +400,8 @@ function showResults() {
387400
console.log("showResults, done, 100%");
388401
}
389402
const viewData = {
390-
message: "Done",
403+
accountColumnIndex: vpData.accountColumnIndex,
404+
message: getDoneMessage(),
391405
percent: 100,
392406
status: Status.SUCCEEDED,
393407
tableHeader: getHeaderRow(),
@@ -397,6 +411,27 @@ function showResults() {
397411
callbackToSetData(viewData);
398412
}
399413

414+
// Tell the time (in minutes) it took to wait, and the time it took to finish (after the wait ended)
415+
function getDoneMessage() {
416+
const finishTime = Date.now();
417+
const minutesWaiting = Math.floor(
418+
(vpData.firstResponseTime - vpData.startTime) / 1000
419+
);
420+
const minutesFurther = Math.floor(
421+
(finishTime - vpData.firstResponseTime) / 1000
422+
);
423+
const minutesTotal = minutesWaiting + minutesFurther;
424+
return (
425+
"Done. Time elapsed = " +
426+
minutesTotal +
427+
" minutes (" +
428+
minutesWaiting +
429+
" waiting for first response + " +
430+
minutesFurther +
431+
" additional)"
432+
);
433+
}
434+
400435
function wasCancelled() {
401436
return latestReq === RequestType.CANCEL;
402437
}
@@ -538,12 +573,4 @@ function addColumnComments(worksheet) {
538573
}
539574
}
540575

541-
/** are we tracking this user? */
542-
function isRegularVetter(user) {
543-
if (user.allLocales || !user.locales) {
544-
return false;
545-
}
546-
return user.userlevelName === "vetter" || user.userlevelName === "guest";
547-
}
548-
549576
export { Status, cancel, hasPermission, saveAsSheet, start, viewMounted };

tools/cldr-apps/js/src/views/VettingParticipation.vue

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ let status = ref(STATUS.INIT);
1212
let tableBody = null;
1313
let tableHeader = null;
1414
let tableComments = null;
15+
let accountColumnIndex = ref(-1);
1516
1617
function mounted() {
1718
cldrVettingParticipation.viewMounted(setData);
@@ -47,14 +48,11 @@ function setData(data) {
4748
if (data.status) {
4849
status.value = data.status;
4950
}
50-
if (data.tableHeader) {
51+
if (data.tableHeader && data.tableBody && data.tableComments) {
5152
tableHeader = reactive(data.tableHeader);
52-
}
53-
if (data.tableBody) {
5453
tableBody = reactive(data.tableBody);
55-
}
56-
if (data.tableComments) {
5754
tableComments = reactive(data.tableComments);
55+
accountColumnIndex.value = data.accountColumnIndex;
5856
}
5957
}
6058
@@ -71,6 +69,10 @@ function progressBarStatus() {
7169
}
7270
}
7371
72+
function accountRecentActivityLink(cell) {
73+
return "#recent_activity///" + cell;
74+
}
75+
7476
function saveAsSheet() {
7577
return cldrVettingParticipation.saveAsSheet();
7678
}
@@ -111,8 +113,15 @@ defineExpose({
111113
</thead>
112114
<tbody>
113115
<tr v-for="row of tableBody" :key="row">
114-
<td v-for="cell of row" :key="cell">
115-
{{ cell }}
116+
<td v-for="(cell, index) of row" :key="cell">
117+
<div v-if="accountColumnIndex == index">
118+
<a-tooltip title="Show recent activity ↗">
119+
<a :href="accountRecentActivityLink(cell)" target="_blank">{{ cell }}</a>
120+
</a-tooltip>
121+
</div>
122+
<div v-else>
123+
{{ cell }}
124+
</div>
116125
</td>
117126
</tr>
118127
</tbody>

tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyVettingParticipation.java

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,27 +71,22 @@ public void getJson(SurveyJSONWrapper r)
7171
while (rsu.next()) {
7272
int theirId = rsu.getInt("id");
7373
final User theUser = sm.reg.getInfo(theirId);
74-
75-
if (UserRegistry.userIsLocked(theUser)
76-
|| UserRegistry.userIsManagerOrStronger(theUser)
77-
|| !UserRegistry.userIsGuestOrStronger(theUser)) {
78-
continue; // skip these
74+
if (!UserRegistry.userIsExactlyVetter(theUser)) {
75+
continue;
76+
}
77+
LocaleSet localeSet = getVettingParticipationLocales(theUser);
78+
if (localeSet == null || localeSet.isEmpty()) {
79+
continue;
7980
}
8081
// Note: this json includes some data not currently (2025-07) used by the
8182
// client for vetting participation, such as user.emailHash and user.time.
8283
final JSONObject json = SurveyJSONWrapper.wrap(theUser);
8384
userObj.put(json);
84-
85-
final LocaleSet intSet = theUser.getInterestLocales();
86-
if (intSet == null || intSet.isEmpty() || intSet.isAllLocales()) {
87-
json.put("allLocales", true);
88-
} else {
89-
JSONArray intArr = new JSONArray();
90-
for (final CLDRLocale l : intSet.getSet()) {
91-
intArr.put(l.getBaseName());
92-
}
93-
json.put("locales", intArr);
85+
JSONArray intArr = new JSONArray();
86+
for (final CLDRLocale l : localeSet.getSet()) {
87+
intArr.put(l.getBaseName());
9488
}
89+
json.put("locales", intArr);
9590
}
9691

9792
rs = psParticipation.executeQuery();
@@ -118,4 +113,23 @@ public void getJson(SurveyJSONWrapper r)
118113
r.put("users", userObj);
119114
r.put("participation", participationObj);
120115
}
116+
117+
/**
118+
* Basically, get the authorized locale set for the given user; however, if that set is "all
119+
* locales", return null unless the user's org has just one language, in which case get that
120+
* language
121+
*
122+
* @param theUser the user in question
123+
* @return the set of locales, or null, but not "all locales"
124+
*/
125+
private LocaleSet getVettingParticipationLocales(User theUser) {
126+
LocaleSet set = theUser.getAuthorizedLocaleSet();
127+
if (set != null && set.isAllLocales()) {
128+
set = theUser.getOrganization().getCoveredLocales();
129+
if (set.isAllLocales() || set.getSet().size() != 1) {
130+
return null;
131+
}
132+
}
133+
return set;
134+
}
121135
}

tools/cldr-apps/src/main/java/org/unicode/cldr/web/UserRegistry.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,10 @@ public static boolean userIsManagerOrStronger(User u) {
18341834
return (u != null) && u.getLevel().isManagerOrStronger();
18351835
}
18361836

1837+
public static boolean userIsExactlyVetter(User u) {
1838+
return (u != null) && u.getLevel().isExactlyVetter();
1839+
}
1840+
18371841
public static boolean userIsVetterOrStronger(User u) {
18381842
return (u != null) && u.getLevel().isVetterOrStronger();
18391843
}

tools/cldr-code/src/main/java/org/unicode/cldr/util/VoteResolver.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ public boolean isManagerOrStronger() {
356356
return stlevel <= manager.stlevel;
357357
}
358358

359+
public boolean isExactlyVetter() {
360+
return stlevel == vetter.stlevel;
361+
}
362+
359363
public boolean isVetterOrStronger() {
360364
return stlevel <= vetter.stlevel;
361365
}

0 commit comments

Comments
 (0)