@capytale/activity.js 3.1.24 → 3.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,14 @@
1
+ type IframeUrlMap = {
2
+ create: string;
3
+ review: string;
4
+ assignment: string;
5
+ view: string;
6
+ };
7
+ type ContractParameters = {
8
+ variant?: string;
9
+ backend?: 'db' | 'fs';
10
+ [p: string]: number | boolean | string | undefined;
11
+ };
1
12
  export type ActivityType = {
2
13
  id: string;
3
14
  name: string;
@@ -27,6 +38,11 @@ export type ActivityType = {
27
38
  exportable: boolean;
28
39
  /** Si ce type d'activité est compatible lti. (`null` signifie ne sera jamais compatible) */
29
40
  lti: boolean | null;
41
+ /** L'url de l'iFrame */
42
+ iframe_url: null | string | IframeUrlMap;
43
+ contracts: null | {
44
+ [contract: string]: ContractParameters;
45
+ };
30
46
  };
31
47
  export type ActivityGroup = {
32
48
  title: string;
@@ -50,3 +66,4 @@ export type ActivityWeights = {
50
66
  };
51
67
  groups: ActivityGroups;
52
68
  };
69
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"activityType.js","sourceRoot":"","sources":["../../src/activity/activityType/activityType.ts"],"names":[],"mappings":"","sourcesContent":["export type ActivityType = {\n id: string;\n name: string;\n status?: { beta?: boolean };\n icon: {\n path: string;\n style?: { [s: string]: string };\n };\n helpUrl: string;\n summary: string;\n description: string;\n tags?: string;\n bundle: string;\n /** Si ce type d'activité est disponible pour l'utilisateur courant. */\n available: boolean;\n /** Indique que ce type d'activité est remplacé. */\n replacedBy?: string | null;\n /** Ce type d'activité devrait être filtré en même temps que celui indiqué. */\n filterWith?: string | null;\n /** Si ce type d'activité produit des évaluations détaillées. */\n detailedEvaluation: boolean;\n /** Si ce type d'activité est exportable. */\n exportable: boolean;\n /** Si ce type d'activité est compatible lti. (`null` signifie ne sera jamais compatible) */\n lti: boolean | null;\n}\n\nexport type ActivityGroup = {\n title: string;\n tooltip?: string;\n activities: string[];\n};\n\nexport type ActivityGroups = {\n [key: string]: ActivityGroup;\n};\n\nexport type ActivityWeights = {\n all: { [key: string]: number };\n niveaux: {\n [key: string]: {\n title: string;\n activities: { [key: string]: number }\n }\n };\n groups: ActivityGroups;\n};\n"]}
1
+ {"version":3,"file":"activityType.js","sourceRoot":"","sources":["../../src/activity/activityType/activityType.ts"],"names":[],"mappings":"","sourcesContent":["import type { ActivityMode } from \"~/activitySession\";\n\ntype IframeUrlMap = {\n create: string;\n review: string;\n assignment: string;\n view: string;\n}\n\ntype ContractParameters = {\n variant?: string;\n backend?: 'db' | 'fs';\n [p: string] : number | boolean | string | undefined;\n}\n\nexport type ActivityType = {\n id: string;\n name: string;\n status?: { beta?: boolean };\n icon: {\n path: string;\n style?: { [s: string]: string };\n };\n helpUrl: string;\n summary: string;\n description: string;\n tags?: string;\n bundle: string;\n /** Si ce type d'activité est disponible pour l'utilisateur courant. */\n available: boolean;\n /** Indique que ce type d'activité est remplacé. */\n replacedBy?: string | null;\n /** Ce type d'activité devrait être filtré en même temps que celui indiqué. */\n filterWith?: string | null;\n /** Si ce type d'activité produit des évaluations détaillées. */\n detailedEvaluation: boolean;\n /** Si ce type d'activité est exportable. */\n exportable: boolean;\n /** Si ce type d'activité est compatible lti. (`null` signifie ne sera jamais compatible) */\n lti: boolean | null;\n /** L'url de l'iFrame */\n \n iframe_url: null | string | IframeUrlMap;\n contracts: null | {\n [contract: string]: ContractParameters;\n }\n}\n\nexport type ActivityGroup = {\n title: string;\n tooltip?: string;\n activities: string[];\n};\n\nexport type ActivityGroups = {\n [key: string]: ActivityGroup;\n};\n\nexport type ActivityWeights = {\n all: { [key: string]: number };\n niveaux: {\n [key: string]: {\n title: string;\n activities: { [key: string]: number }\n }\n };\n groups: ActivityGroups;\n};\n"]}
@@ -1,5 +1,6 @@
1
1
  import { toIsoString } from "../../util/isoDate";
2
2
  import iter from "../../util/iterator";
3
+ import { createIntegrator } from "./integration";
3
4
  function checkEvaluation(evaluation) {
4
5
  let date;
5
6
  if (evaluation.date == null) {
@@ -34,27 +35,12 @@ export default function (hb) {
34
35
  return data.filter(sa => sa.hidden);
35
36
  return data.filter(sa => !sa.hidden);
36
37
  }
37
- function simplifyExerciceLabels(evals) {
38
- for (const e of evals) {
39
- const prefix = e.evalLabel.slice(0, 9); //"Exercice "
40
- if (prefix === "Exercice ") {
41
- e.evalLabel = "Ex." + e.evalLabel.slice(9);
42
- }
43
- e.serverDate = new Date(1000 * e.serverDate);
44
- if (e.date != null)
45
- e.date = new Date(e.date);
46
- else
47
- e.date = e.serverDate;
48
- }
49
- }
50
38
  async function getEvalsForStudent(id, uid) {
51
39
  const data = (await hb.getJsonAsync({ name: 'sa.eval', id, uid }));
52
- simplifyExerciceLabels(data.evals);
53
40
  return data;
54
41
  }
55
42
  async function getEvalsForAssignment(id) {
56
43
  const data = (await hb.getJsonAsync({ name: 'sa.eval', id }));
57
- simplifyExerciceLabels(data.evals);
58
44
  return data;
59
45
  }
60
46
  return {
@@ -63,9 +49,9 @@ export default function (hb) {
63
49
  },
64
50
  async listEvals(id, uids) {
65
51
  const saList = await listSa(id, 'all');
66
- const saByUid = {};
52
+ const saMap = {};
67
53
  for (const sa of saList) {
68
- saByUid[sa.uid] = sa;
54
+ saMap[sa.uid] = sa;
69
55
  }
70
56
  if (uids == null) {
71
57
  uids = saList.map(s => s.uid);
@@ -73,139 +59,38 @@ export default function (hb) {
73
59
  else if (!Array.isArray(uids)) {
74
60
  uids = [uids];
75
61
  }
76
- // Les uid doivent exiter dans saByUid et être uniques
77
- const filteredUidsMap = {};
78
- const filteredUids = [];
62
+ // Les uid doivent exister dans saMap et être uniques
63
+ const filteredUidsSet = new Set();
64
+ const students = [];
79
65
  for (const uid of uids) {
80
- if (filteredUidsMap[uid])
66
+ if (filteredUidsSet.has(uid))
81
67
  continue;
82
- if (!saByUid[uid])
68
+ if (!saMap[uid])
83
69
  continue;
84
- filteredUidsMap[uid] = true;
85
- filteredUids.push(uid);
70
+ filteredUidsSet.add(uid);
71
+ const info = saMap[uid];
72
+ students.push({
73
+ firstname: info.firstname,
74
+ lastname: info.lastname,
75
+ classe: info.classe,
76
+ uid,
77
+ nid: info.nid,
78
+ });
86
79
  }
87
- const studentsByUid = {};
88
- const evaluationsByLabel = {};
89
- const evaluationLabels = [];
90
- const sortRegEx = /(\d+)$/;
91
- for (const [uid, itemsPromise] of iter(filteredUids, uid => getEvalsForAssignment(saByUid[uid].nid))) {
92
- if (!studentsByUid[uid]) {
93
- studentsByUid[uid] = {
94
- firstname: saByUid[uid].firstname,
95
- lastname: saByUid[uid].lastname,
96
- classe: saByUid[uid].classe,
97
- uid,
98
- evalsByLabel: {},
99
- };
100
- }
101
- const student = studentsByUid[uid];
102
- const evalData = await itemsPromise;
80
+ const integrator = createIntegrator();
81
+ for (const [student, evalsPromise] of iter(students, s => getEvalsForAssignment(s.nid))) {
82
+ const evals = await evalsPromise;
83
+ integrator.addEval(student, evals.evals);
103
84
  student.review = {
104
- evaluation: evalData.review.evaluation || '',
105
- appreciation: evalData.review.appreciation || '',
85
+ evaluation: evals.review.evaluation,
86
+ appreciation: evals.review.appreciation,
106
87
  };
107
- for (const item of evalData.evals) {
108
- const e = evaluationsByLabel[item.evalLabel];
109
- if (!e) {
110
- const match = item.evalLabel.match(sortRegEx);
111
- let index;
112
- let baseLabel;
113
- if (match) {
114
- index = parseInt(match[0]);
115
- if (Number.isNaN(index)) {
116
- index = NaN;
117
- baseLabel = item.evalLabel;
118
- }
119
- else {
120
- baseLabel = item.evalLabel.slice(0, match.index);
121
- }
122
- }
123
- else {
124
- index = NaN;
125
- baseLabel = item.evalLabel;
126
- }
127
- baseLabel = baseLabel.trim().toLocaleLowerCase();
128
- evaluationLabels.push(item.evalLabel);
129
- evaluationsByLabel[item.evalLabel] = {
130
- label: item.evalLabel,
131
- title: item.evalTitle,
132
- scoreMax: item.scoreMax,
133
- index,
134
- baseLabel,
135
- };
136
- }
137
- else {
138
- if (item.evalTitle != null)
139
- e.title = item.evalTitle;
140
- if ((item.scoreMax != null) && (e.scoreMax != null) && (e.scoreMax < item.scoreMax))
141
- e.scoreMax = item.scoreMax;
142
- }
143
- student.evalsByLabel[item.evalLabel] = {
144
- score: (item.score == null) ? null : item.score,
145
- date: item.date,
146
- comment: item.comment || null,
147
- revision: item.revisionNumber,
148
- scoreLiteral: item.scoreLiteral || null,
149
- };
150
- }
151
88
  }
152
- evaluationLabels.sort((a, b) => {
153
- if (a === b)
154
- return 0;
155
- const aEval = evaluationsByLabel[a];
156
- const bEval = evaluationsByLabel[b];
157
- const diff = aEval.baseLabel.localeCompare(bEval.baseLabel);
158
- if (diff !== 0)
159
- return diff;
160
- if (Number.isNaN(aEval.index)) {
161
- if (Number.isNaN(bEval.index)) {
162
- return a < b ? -1 : 1;
163
- }
164
- else {
165
- return -1;
166
- }
167
- }
168
- else {
169
- if (Number.isNaN(bEval.index)) {
170
- return 1;
171
- }
172
- else {
173
- const diff = aEval.index - bEval.index;
174
- if (diff === 0)
175
- return a < b ? -1 : 1;
176
- return diff;
177
- }
178
- }
179
- });
180
- let totalMax = null;
181
- const evaluations = evaluationLabels.map(label => {
182
- const e = evaluationsByLabel[label];
183
- if (e.scoreMax != null) {
184
- if (totalMax == null)
185
- totalMax = 0;
186
- totalMax += e.scoreMax;
187
- }
188
- delete e.index;
189
- delete e.baseLabel;
190
- return e;
191
- });
192
- const students = filteredUids.map(uid => {
193
- const ebl = studentsByUid[uid].evalsByLabel;
194
- delete studentsByUid[uid].evalsByLabel;
195
- let total = null;
196
- studentsByUid[uid].evaluations = evaluationLabels.map(label => {
197
- const e = ebl[label];
198
- if (e == null)
199
- return { score: null, scoreLiteral: null, date: null, comment: null, revision: null };
200
- if (e.score != null) {
201
- if (total == null)
202
- total = 0;
203
- total += e.score;
204
- }
205
- return e;
206
- });
207
- studentsByUid[uid].totalScore = total;
208
- return studentsByUid[uid];
89
+ const { totalScoreMax: totalMax, evaluations, students: studentMap } = integrator.getIntegratedData();
90
+ students.forEach(student => {
91
+ const ebl = studentMap.get(student);
92
+ student.evaluations = ebl.evaluations;
93
+ student.totalScore = ebl.totalScore;
209
94
  });
210
95
  return {
211
96
  get totalScoreMax() { return totalMax; },
@@ -1 +1 @@
1
- {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/activity/evaluation/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Evaluation, EvaluationItem, SaInfo, EvaluationList } from \"./evaluation\";\nimport type { HttpBackend } from \"~api/http/backend\";\nimport { toIsoString } from \"~util/isoDate\";\nimport iter from \"~util/iterator\";\n\ntype filter = 'all' | 'hidden' | 'visible';\n\nexport type EvaluationBackend = {\n /**\n * Récupère la liste des assignments pour une activité donnée\n * \n * @param id le nid de l'activityNode\n * @return la liste des assignments\n */\n listSa(id: number, filter?: filter): Promise<SaInfo[]>;\n\n /**\n * Récupère les évaluations d'un élève dans une activité donnée\n * \n * @param id le nid de l'activityNode\n * @param uids? le uid de l'élève ou un tableau de uids. Si non spécifié, tous les élèves sont retournés\n * @returns la liste des évaluations\n */\n listEvals(id: number, uid?: number | number[]): Promise<EvaluationList>;\n\n /**\n * Envoie une évaluation pour sauvegarde en backend\n * \n * @param id le nid de l'assignmentNode\n * @param evaluation l'évaluation\n */\n post(id: number, evaluation: Evaluation | Evaluation[]): Promise<void>;\n}\n\nfunction checkEvaluation(evaluation: Evaluation): void {\n let date: Date;\n if (evaluation.date == null) {\n date = new Date();\n } else if (typeof evaluation.date === 'string') {\n date = new Date(evaluation.date);\n } else if (evaluation.date instanceof Date) {\n date = evaluation.date;\n } else {\n throw new Error('Invalid date');\n }\n evaluation.date = toIsoString(date);\n}\n\nexport default function (hb: HttpBackend): EvaluationBackend {\n const listSaCache: { id?: number, data?: SaInfo[] } = {};\n\n async function listSa(id: number, filter?: filter): Promise<SaInfo[]> {\n let data: SaInfo[];\n if ((listSaCache.id === id) && (listSaCache.data != null)) {\n data = listSaCache.data;\n } else {\n data = (await hb.getJsonAsync<SaInfo[]>({ name: 'sa.list', id }))!;\n listSaCache.id = id;\n listSaCache.data = data;\n }\n if (filter === 'all') return data;\n if (filter === 'hidden') return data.filter(sa => sa.hidden);\n return data.filter(sa => !sa.hidden);\n }\n\n type EvalsReturn = { evals: EvaluationItem[], review: { evaluation: string | null, appreciation: string | null } };\n\n function simplifyExerciceLabels(evals: EvaluationItem[]): void {\n for (const e of evals) {\n const prefix = e.evalLabel.slice(0, 9); //\"Exercice \"\n if (prefix === \"Exercice \") {\n e.evalLabel = \"Ex.\" + e.evalLabel.slice(9);\n }\n e.serverDate = new Date(1000 * (e.serverDate as any));\n if (e.date != null) e.date = new Date(e.date);\n else e.date = e.serverDate;\n }\n }\n\n async function getEvalsForStudent(id: number, uid: number): Promise<EvalsReturn> {\n const data = (await hb.getJsonAsync<EvalsReturn>({ name: 'sa.eval', id, uid }))!;\n simplifyExerciceLabels(data.evals);\n return data;\n }\n\n async function getEvalsForAssignment(id: number): Promise<EvalsReturn> {\n const data = (await hb.getJsonAsync<EvalsReturn>({ name: 'sa.eval', id }))!;\n simplifyExerciceLabels(data.evals);\n return data;\n }\n\n return {\n listSa(id: number, filter?: filter): Promise<SaInfo[]> {\n return listSa(id, filter);\n },\n async listEvals(id: number, uids?: number | number[]): Promise<EvaluationList> {\n const saList = await listSa(id, 'all');\n const saByUid: { [uid: number]: SaInfo } = {};\n for (const sa of saList) {\n saByUid[sa.uid] = sa;\n }\n if (uids == null) {\n uids = saList.map(s => s.uid);\n } else if (!Array.isArray(uids)) {\n uids = [uids];\n }\n\n // Les uid doivent exiter dans saByUid et être uniques\n const filteredUidsMap: { [uid: number]: boolean } = {};\n const filteredUids: number[] = [];\n for (const uid of uids) {\n if (filteredUidsMap[uid]) continue;\n if (!saByUid[uid]) continue;\n filteredUidsMap[uid] = true;\n filteredUids.push(uid);\n }\n\n const studentsByUid: { [uid: number]: EvaluationList['students'][number] & { evalsByLabel?: { [label: string]: EvaluationList['students'][number]['evaluations'][number] } } } = {};\n const evaluationsByLabel: { [label: string]: EvaluationList['evaluations'][number] & { index?: number, baseLabel?: string } } = {};\n const evaluationLabels: string[] = [];\n const sortRegEx = /(\\d+)$/;\n for (const [uid, itemsPromise] of iter(filteredUids, uid => getEvalsForAssignment(saByUid[uid].nid))) {\n if (!studentsByUid[uid]) {\n studentsByUid[uid] = {\n firstname: saByUid[uid].firstname,\n lastname: saByUid[uid].lastname,\n classe: saByUid[uid].classe,\n uid,\n evalsByLabel: {},\n } as EvaluationList['students'][number] & { evalsByLabel?: { [label: string]: EvaluationList['students'][number]['evaluations'][number] } };\n }\n const student = studentsByUid[uid];\n const evalData = await itemsPromise;\n student.review = {\n evaluation: evalData.review.evaluation || '',\n appreciation: evalData.review.appreciation || '',\n };\n for (const item of evalData.evals) {\n const e = evaluationsByLabel[item.evalLabel];\n if (!e) {\n const match = item.evalLabel.match(sortRegEx);\n let index: number;\n let baseLabel: string;\n if (match) {\n index = parseInt(match[0]);\n if (Number.isNaN(index)) {\n index = NaN;\n baseLabel = item.evalLabel;\n } else {\n baseLabel = item.evalLabel.slice(0, match.index);\n }\n } else {\n index = NaN;\n baseLabel = item.evalLabel;\n }\n baseLabel = baseLabel.trim().toLocaleLowerCase();\n\n evaluationLabels.push(item.evalLabel);\n evaluationsByLabel[item.evalLabel] = {\n label: item.evalLabel,\n title: item.evalTitle,\n scoreMax: item.scoreMax,\n index,\n baseLabel,\n };\n } else {\n if (item.evalTitle != null) e.title = item.evalTitle;\n if ((item.scoreMax != null) && (e.scoreMax != null) && (e.scoreMax < item.scoreMax)) e.scoreMax = item.scoreMax;\n }\n student.evalsByLabel![item.evalLabel] = {\n score: (item.score == null) ? null : item.score,\n date: item.date,\n comment: item.comment || null,\n revision: item.revisionNumber,\n scoreLiteral: item.scoreLiteral || null,\n };\n }\n }\n evaluationLabels.sort((a, b) => {\n if (a === b) return 0;\n const aEval = evaluationsByLabel[a];\n const bEval = evaluationsByLabel[b];\n const diff = aEval.baseLabel!.localeCompare(bEval.baseLabel!);\n if (diff !== 0) return diff;\n if (Number.isNaN(aEval.index)) {\n if (Number.isNaN(bEval.index)) {\n return a < b ? -1 : 1;\n } else {\n return -1;\n }\n } else {\n if (Number.isNaN(bEval.index)) {\n return 1;\n } else {\n const diff = aEval.index! - bEval.index!;\n if (diff === 0) return a < b ? -1 : 1;\n return diff;\n }\n }\n });\n let totalMax: number | null = null;\n const evaluations = evaluationLabels.map(label => {\n const e = evaluationsByLabel[label];\n if (e.scoreMax != null) {\n if (totalMax == null) totalMax = 0;\n totalMax += e.scoreMax;\n }\n delete e.index;\n delete e.baseLabel;\n return e;\n });\n const students = filteredUids.map(uid => {\n const ebl = studentsByUid[uid].evalsByLabel!;\n delete studentsByUid[uid].evalsByLabel;\n let total: number | null = null;\n studentsByUid[uid].evaluations = evaluationLabels.map(label => {\n const e = ebl[label];\n if (e == null) return { score: null, scoreLiteral: null, date: null, comment: null, revision: null };\n if (e.score != null) {\n if (total == null) total = 0;\n total += e.score;\n }\n return e;\n });\n studentsByUid[uid].totalScore = total;\n return studentsByUid[uid];\n });\n return {\n get totalScoreMax() { return totalMax; },\n get evaluations() { return evaluations; },\n get students() { return students; },\n };\n },\n post(id: number, evaluation: Evaluation | Evaluation[]): Promise<void> {\n if (Array.isArray(evaluation)) {\n for (const e of evaluation) {\n checkEvaluation(e);\n }\n } else {\n checkEvaluation(evaluation);\n }\n return hb.postJsonAsync({ name: 'sa.eval', id }, evaluation);\n },\n };\n}"]}
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/activity/evaluation/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Evaluation, EvaluationItem, SaInfo, EvaluationList } from \"./evaluation\";\nimport type { HttpBackend } from \"~api/http/backend\";\nimport { toIsoString } from \"~util/isoDate\";\nimport iter from \"~util/iterator\";\n\nimport { createIntegrator } from \"./integration\";\n\ntype filter = 'all' | 'hidden' | 'visible';\n\nexport type EvaluationBackend = {\n /**\n * Récupère la liste des assignments pour une activité donnée\n * \n * @param id le nid de l'activityNode\n * @return la liste des assignments\n */\n listSa(id: number, filter?: filter): Promise<SaInfo[]>;\n\n /**\n * Récupère les évaluations d'un élève dans une activité donnée\n * \n * @param id le nid de l'activityNode\n * @param uids? le uid de l'élève ou un tableau de uids. Si non spécifié, tous les élèves sont retournés\n * @returns la liste des évaluations\n */\n listEvals(id: number, uid?: number | number[]): Promise<EvaluationList>;\n\n /**\n * Envoie une évaluation pour sauvegarde en backend\n * \n * @param id le nid de l'assignmentNode\n * @param evaluation l'évaluation\n */\n post(id: number, evaluation: Evaluation | Evaluation[]): Promise<void>;\n}\n\nfunction checkEvaluation(evaluation: Evaluation): void {\n let date: Date;\n if (evaluation.date == null) {\n date = new Date();\n } else if (typeof evaluation.date === 'string') {\n date = new Date(evaluation.date);\n } else if (evaluation.date instanceof Date) {\n date = evaluation.date;\n } else {\n throw new Error('Invalid date');\n }\n evaluation.date = toIsoString(date);\n}\n\nexport default function (hb: HttpBackend): EvaluationBackend {\n const listSaCache: { id?: number, data?: SaInfo[] } = {};\n\n async function listSa(id: number, filter?: filter): Promise<SaInfo[]> {\n let data: SaInfo[];\n if ((listSaCache.id === id) && (listSaCache.data != null)) {\n data = listSaCache.data;\n } else {\n data = (await hb.getJsonAsync<SaInfo[]>({ name: 'sa.list', id }))!;\n listSaCache.id = id;\n listSaCache.data = data;\n }\n if (filter === 'all') return data;\n if (filter === 'hidden') return data.filter(sa => sa.hidden);\n return data.filter(sa => !sa.hidden);\n }\n\n type EvalsReturn = { evals: EvaluationItem[], review: { evaluation: string | null, appreciation: string | null } };\n\n async function getEvalsForStudent(id: number, uid: number): Promise<EvalsReturn> {\n const data = (await hb.getJsonAsync<EvalsReturn>({ name: 'sa.eval', id, uid }))!;\n return data;\n }\n\n async function getEvalsForAssignment(id: number): Promise<EvalsReturn> {\n const data = (await hb.getJsonAsync<EvalsReturn>({ name: 'sa.eval', id }))!;\n return data;\n }\n\n return {\n listSa(id: number, filter?: filter): Promise<SaInfo[]> {\n return listSa(id, filter);\n },\n async listEvals(id: number, uids?: number | number[]): Promise<EvaluationList> {\n const saList = await listSa(id, 'all');\n const saMap: { [uid: number]: SaInfo } = {};\n for (const sa of saList) {\n saMap[sa.uid] = sa;\n }\n if (uids == null) {\n uids = saList.map(s => s.uid);\n } else if (!Array.isArray(uids)) {\n uids = [uids];\n }\n\n type StudentEval = EvaluationList['students'][number];\n // Les uid doivent exister dans saMap et être uniques\n const filteredUidsSet = new Set<number>();\n const students: StudentEval[] = [];\n for (const uid of uids) {\n if (filteredUidsSet.has(uid)) continue;\n if (!saMap[uid]) continue;\n filteredUidsSet.add(uid);\n const info = saMap[uid];\n students.push({\n firstname: info.firstname,\n lastname: info.lastname,\n classe: info.classe,\n uid,\n nid: info.nid,\n } as any);\n }\n\n const integrator = createIntegrator<StudentEval>();\n for (const [student, evalsPromise] of iter(students, s => getEvalsForAssignment(s.nid))) {\n const evals = await evalsPromise;\n integrator.addEval(student, evals.evals);\n student.review = {\n evaluation: evals.review.evaluation,\n appreciation: evals.review.appreciation,\n };\n }\n\n const { totalScoreMax: totalMax, evaluations, students: studentMap } = integrator.getIntegratedData();\n\n students.forEach(student => {\n const ebl = studentMap.get(student)!;\n student.evaluations = ebl.evaluations\n student.totalScore = ebl.totalScore\n });\n\n\n return {\n get totalScoreMax() { return totalMax; },\n get evaluations() { return evaluations; },\n get students() { return students; },\n };\n },\n post(id: number, evaluation: Evaluation | Evaluation[]): Promise<void> {\n if (Array.isArray(evaluation)) {\n for (const e of evaluation) {\n checkEvaluation(e);\n }\n } else {\n checkEvaluation(evaluation);\n }\n return hb.postJsonAsync({ name: 'sa.eval', id }, evaluation);\n },\n };\n}"]}
@@ -45,12 +45,17 @@ export type EvaluationList = {
45
45
  lastname: string;
46
46
  classe: string;
47
47
  uid: number;
48
+ nid: number;
48
49
  review: {
49
50
  evaluation: string | null;
50
51
  appreciation: string | null;
51
52
  };
52
53
  evaluations: {
53
54
  score: number | null;
55
+ originalScore?: {
56
+ score: number;
57
+ scoreMax: number;
58
+ };
54
59
  scoreLiteral: string | null;
55
60
  date: Date | null;
56
61
  comment: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"evaluation.js","sourceRoot":"","sources":["../../src/activity/evaluation/evaluation.ts"],"names":[],"mappings":"","sourcesContent":["type NumericEvaluation = {\n score: number;\n scoreMax: number;\n}\n\ntype StringEvaluation = {\n score: string;\n}\n\nexport type Evaluation = {\n evalLabel?: string;\n evalTitle?: string;\n comment?: string;\n date?: Date | string;\n} & (NumericEvaluation | StringEvaluation);\n\nexport type EvaluationItem = {\n evalLabel: string;\n score?: number;\n scoreMax?: number;\n scoreLiteral?: string;\n uid: number;\n graderUid: number;\n serverDate: Date;\n evalTitle?: string;\n comment?: string;\n date: Date;\n revisionNumber: number;\n}\n\nexport type SaInfo = {\n nid: number,\n uid: number,\n lastname: string,\n firstname: string,\n classe: string,\n workflow: string,\n hidden: boolean,\n};\n\nexport type EvaluationList = {\n totalScoreMax: number | null;\n evaluations: {\n label: string;\n title?: string;\n scoreMax?: number;\n }[];\n students: {\n firstname: string;\n lastname: string;\n classe: string;\n uid: number;\n review: {\n evaluation: string | null;\n appreciation: string | null;\n };\n evaluations: {\n score: number | null;\n scoreLiteral: string | null;\n date: Date | null;\n comment: string | null;\n revision: number | null;\n }[];\n totalScore: number|null;\n }[]\n}"]}
1
+ {"version":3,"file":"evaluation.js","sourceRoot":"","sources":["../../src/activity/evaluation/evaluation.ts"],"names":[],"mappings":"","sourcesContent":["type NumericEvaluation = {\n score: number;\n scoreMax: number;\n}\n\ntype StringEvaluation = {\n score: string;\n}\n\nexport type Evaluation = {\n evalLabel?: string;\n evalTitle?: string;\n comment?: string;\n date?: Date | string;\n} & (NumericEvaluation | StringEvaluation);\n\nexport type EvaluationItem = {\n evalLabel: string;\n score?: number;\n scoreMax?: number;\n scoreLiteral?: string;\n uid: number;\n graderUid: number;\n serverDate: Date;\n evalTitle?: string;\n comment?: string;\n date: Date;\n revisionNumber: number;\n}\n\nexport type SaInfo = {\n nid: number,\n uid: number,\n lastname: string,\n firstname: string,\n classe: string,\n workflow: string,\n hidden: boolean,\n};\n\nexport type EvaluationList = {\n totalScoreMax: number | null;\n evaluations: {\n label: string;\n title?: string;\n scoreMax?: number;\n }[];\n students: {\n firstname: string;\n lastname: string;\n classe: string;\n uid: number;\n nid: number;\n review: {\n evaluation: string | null;\n appreciation: string | null;\n };\n evaluations: {\n score: number | null;\n originalScore?: {\n score: number;\n scoreMax: number;\n }\n scoreLiteral: string | null;\n date: Date | null;\n comment: string | null;\n revision: number | null;\n }[];\n totalScore: number|null;\n }[]\n}"]}
@@ -0,0 +1,32 @@
1
+ import type { EvaluationItem } from "./evaluation";
2
+ type EvalHeader = {
3
+ label: string;
4
+ originalLabel: string;
5
+ title?: string;
6
+ scoreMax?: number;
7
+ };
8
+ type StudentEvalItem = {
9
+ score: number | null;
10
+ originalScore?: {
11
+ score: number;
12
+ scoreMax: number;
13
+ };
14
+ scoreLiteral: string | null;
15
+ date: Date | null;
16
+ comment: string | null;
17
+ revision: number | null;
18
+ };
19
+ type StudentEval = {
20
+ evaluations: StudentEvalItem[];
21
+ totalScore: number | null;
22
+ };
23
+ type IntegratedEvaluation<T> = {
24
+ totalScoreMax: number | null;
25
+ evaluations: EvalHeader[];
26
+ students: Map<T, StudentEval>;
27
+ };
28
+ export declare function createIntegrator<T>(): {
29
+ addEval(student: T, evals: EvaluationItem[]): void;
30
+ getIntegratedData(): IntegratedEvaluation<T>;
31
+ };
32
+ export {};
@@ -0,0 +1,143 @@
1
+ export function createIntegrator() {
2
+ const sortRegEx = /(\d+)$/;
3
+ function prepareEval(e) {
4
+ const match = e.evalLabel.match(sortRegEx);
5
+ let index;
6
+ let baseLabel;
7
+ if (match) {
8
+ index = parseInt(match[0]);
9
+ if (Number.isNaN(index)) {
10
+ index = NaN;
11
+ baseLabel = e.evalLabel;
12
+ }
13
+ else {
14
+ baseLabel = e.evalLabel.slice(0, match.index);
15
+ }
16
+ }
17
+ else {
18
+ index = NaN;
19
+ baseLabel = e.evalLabel;
20
+ }
21
+ baseLabel = baseLabel.trim().toLocaleLowerCase();
22
+ return {
23
+ label: (e.evalLabel.slice(0, 9) === "Exercice ") ? "Ex." + e.evalLabel.slice(9) : e.evalLabel,
24
+ originalLabel: e.evalLabel,
25
+ title: e.evalTitle,
26
+ scoreMax: e.scoreMax,
27
+ index,
28
+ baseLabel,
29
+ };
30
+ }
31
+ const studentMap = new Map();
32
+ const evaluationsByLabel = new Map();
33
+ function addEval(student, evals) {
34
+ const studentEval = {
35
+ totalScore: null,
36
+ evalMap: new Map(),
37
+ };
38
+ studentMap.set(student, studentEval);
39
+ for (const item of evals) {
40
+ let e = evaluationsByLabel.get(item.evalLabel);
41
+ if (!e) {
42
+ e = prepareEval(item);
43
+ evaluationsByLabel.set(item.evalLabel, e);
44
+ }
45
+ else {
46
+ if (item.evalTitle != null)
47
+ e.title = item.evalTitle;
48
+ if ((item.scoreMax != null) && (e.scoreMax != null) && (e.scoreMax < item.scoreMax))
49
+ e.scoreMax = item.scoreMax;
50
+ }
51
+ const date = (item.date == null)
52
+ ? new Date(1000 * item.serverDate)
53
+ : new Date(item.date);
54
+ const evalItem = {
55
+ date,
56
+ comment: item.comment || null,
57
+ revision: item.revisionNumber,
58
+ scoreLiteral: item.scoreLiteral || null,
59
+ };
60
+ if (item.score != null && item.scoreMax != null) {
61
+ evalItem.originalScore = {
62
+ score: item.score,
63
+ scoreMax: item.scoreMax,
64
+ };
65
+ }
66
+ studentEval.evalMap.set(e, evalItem);
67
+ }
68
+ }
69
+ function getIntegratedData() {
70
+ const evaluationsList = [...evaluationsByLabel.values()].sort((aEval, bEval) => {
71
+ if (aEval === bEval)
72
+ return 0;
73
+ const diff = aEval.baseLabel.localeCompare(bEval.baseLabel);
74
+ if (diff !== 0)
75
+ return diff;
76
+ if (Number.isNaN(aEval.index)) {
77
+ if (Number.isNaN(bEval.index)) {
78
+ return aEval.label.localeCompare(bEval.label);
79
+ }
80
+ else {
81
+ return -1;
82
+ }
83
+ }
84
+ else {
85
+ if (Number.isNaN(bEval.index)) {
86
+ return 1;
87
+ }
88
+ else {
89
+ const diff = aEval.index - bEval.index;
90
+ if (diff === 0)
91
+ return aEval.label.localeCompare(bEval.label);
92
+ return diff;
93
+ }
94
+ }
95
+ });
96
+ let totalMax = null;
97
+ evaluationsList.forEach(e => {
98
+ if (e.scoreMax != null) {
99
+ if (totalMax == null)
100
+ totalMax = e.scoreMax;
101
+ else
102
+ totalMax += e.scoreMax;
103
+ }
104
+ delete e.index;
105
+ delete e.baseLabel;
106
+ });
107
+ studentMap.forEach(studentEval => {
108
+ studentEval.evaluations = evaluationsList.map(eh => {
109
+ const evalItem = studentEval.evalMap.get(eh);
110
+ if (evalItem == null)
111
+ return { score: null, scoreLiteral: null, date: null, comment: null, revision: null };
112
+ if (evalItem.originalScore == null) {
113
+ evalItem.score = null;
114
+ }
115
+ else {
116
+ if (eh.scoreMax === evalItem.originalScore.scoreMax) {
117
+ evalItem.score = evalItem.originalScore.score;
118
+ delete evalItem.originalScore;
119
+ }
120
+ else {
121
+ evalItem.score = evalItem.originalScore.score * eh.scoreMax / evalItem.originalScore.scoreMax;
122
+ }
123
+ if (studentEval.totalScore == null)
124
+ studentEval.totalScore = evalItem.score;
125
+ else
126
+ studentEval.totalScore += evalItem.score;
127
+ }
128
+ return evalItem;
129
+ });
130
+ delete studentEval.evalMap;
131
+ });
132
+ return {
133
+ get totalScoreMax() { return totalMax; },
134
+ get evaluations() { return evaluationsList; },
135
+ get students() { return studentMap; },
136
+ };
137
+ }
138
+ return {
139
+ addEval,
140
+ getIntegratedData,
141
+ };
142
+ }
143
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.js","sourceRoot":"","sources":["../../src/activity/evaluation/integration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EvaluationItem } from \"./evaluation\";\n\ntype EvalHeader = {\n label: string;\n originalLabel: string;\n title?: string;\n scoreMax?: number;\n}\n\ntype StudentEvalItem = {\n score: number | null;\n originalScore?: {\n score: number;\n scoreMax: number;\n }\n scoreLiteral: string | null;\n date: Date | null;\n comment: string | null;\n revision: number | null;\n}\n\ntype StudentEval = {\n evaluations: StudentEvalItem[];\n totalScore: number | null;\n}\n\ntype IntegratedEvaluation<T> = {\n totalScoreMax: number | null;\n evaluations: EvalHeader[];\n students: Map<T, StudentEval>;\n}\n\nexport function createIntegrator<T>(): {\n addEval(student: T, evals: EvaluationItem[]): void;\n getIntegratedData(): IntegratedEvaluation<T>\n} {\n type EvalHeader2 = EvalHeader & {\n index?: number;\n baseLabel?: string;\n }\n\n type StudentEval2 = StudentEval & {\n evalMap?: Map<EvalHeader2, StudentEvalItem>;\n }\n\n const sortRegEx = /(\\d+)$/;\n function prepareEval(e: EvaluationItem): EvalHeader2 {\n const match = e.evalLabel.match(sortRegEx);\n let index: number;\n let baseLabel: string;\n if (match) {\n index = parseInt(match[0]);\n if (Number.isNaN(index)) {\n index = NaN;\n baseLabel = e.evalLabel;\n } else {\n baseLabel = e.evalLabel.slice(0, match.index);\n }\n } else {\n index = NaN;\n baseLabel = e.evalLabel;\n }\n baseLabel = baseLabel.trim().toLocaleLowerCase();\n return {\n label: (e.evalLabel.slice(0, 9) === \"Exercice \") ? \"Ex.\" + e.evalLabel.slice(9) : e.evalLabel,\n originalLabel: e.evalLabel,\n title: e.evalTitle,\n scoreMax: e.scoreMax,\n index,\n baseLabel,\n };\n }\n\n const studentMap = new Map<T, StudentEval2>();\n const evaluationsByLabel = new Map<string, EvalHeader2>();\n\n function addEval(student: T, evals: EvaluationItem[]) {\n const studentEval = {\n totalScore: null,\n evalMap: new Map(),\n } as StudentEval2;\n studentMap.set(student, studentEval);\n for (const item of evals) {\n let e = evaluationsByLabel.get(item.evalLabel);\n if (!e) {\n e = prepareEval(item);\n evaluationsByLabel.set(item.evalLabel, e);\n } else {\n if (item.evalTitle != null) e.title = item.evalTitle;\n if ((item.scoreMax != null) && (e.scoreMax != null) && (e.scoreMax < item.scoreMax)) e.scoreMax = item.scoreMax;\n }\n const date = (item.date == null)\n ? new Date(1000 * (item.serverDate as any))\n : new Date(item.date);\n\n const evalItem: StudentEvalItem = {\n date,\n comment: item.comment || null,\n revision: item.revisionNumber,\n scoreLiteral: item.scoreLiteral || null,\n } as StudentEvalItem;\n if (item.score != null && item.scoreMax != null) {\n evalItem.originalScore = {\n score: item.score,\n scoreMax: item.scoreMax,\n };\n }\n studentEval.evalMap!.set(e, evalItem);\n }\n }\n\n function getIntegratedData() {\n const evaluationsList = [...evaluationsByLabel.values()].sort((aEval, bEval) => {\n if (aEval === bEval) return 0;\n const diff = aEval.baseLabel!.localeCompare(bEval.baseLabel!);\n if (diff !== 0) return diff;\n if (Number.isNaN(aEval.index)) {\n if (Number.isNaN(bEval.index)) {\n return aEval.label.localeCompare(bEval.label);\n } else {\n return -1;\n }\n } else {\n if (Number.isNaN(bEval.index)) {\n return 1;\n } else {\n const diff = aEval.index! - bEval.index!;\n if (diff === 0) return aEval.label.localeCompare(bEval.label);\n return diff;\n }\n }\n });\n let totalMax: number | null = null;\n evaluationsList.forEach(e => {\n if (e.scoreMax != null) {\n if (totalMax == null) totalMax = e.scoreMax;\n else totalMax += e.scoreMax;\n }\n delete e.index;\n delete e.baseLabel;\n });\n studentMap.forEach(studentEval => {\n studentEval.evaluations = evaluationsList.map(eh => {\n const evalItem = studentEval.evalMap!.get(eh);\n if (evalItem == null) return { score: null, scoreLiteral: null, date: null, comment: null, revision: null };\n if (evalItem.originalScore == null) {\n evalItem.score = null;\n } else {\n if (eh.scoreMax === evalItem.originalScore.scoreMax) {\n evalItem.score = evalItem.originalScore.score;\n delete evalItem.originalScore;\n } else {\n evalItem.score = evalItem.originalScore.score * eh.scoreMax! / evalItem.originalScore.scoreMax;\n }\n if (studentEval.totalScore == null) studentEval.totalScore = evalItem.score;\n else studentEval.totalScore += evalItem.score;\n }\n return evalItem;\n });\n delete studentEval.evalMap;\n });\n return {\n get totalScoreMax() { return totalMax; },\n get evaluations() { return evaluationsList; },\n get students() { return studentMap; },\n };\n }\n return {\n addEval,\n getIntegratedData,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capytale/activity.js",
3
- "version": "3.1.24",
3
+ "version": "3.1.26",
4
4
  "author": "Capytale.fr",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "bin": {
@@ -14,15 +14,15 @@
14
14
  "clean": "rm -rf tsconfig.tsbuildinfo index.* common/ api/ entity/ backend/ activity/ tracker/ util/ auth/ bin/"
15
15
  },
16
16
  "devDependencies": {
17
- "@rollup/plugin-commonjs": "^28.0.2",
18
- "@rollup/plugin-node-resolve": "^16.0.0",
17
+ "@rollup/plugin-commonjs": "^28.0.3",
18
+ "@rollup/plugin-node-resolve": "^16.0.1",
19
19
  "@rollup/plugin-typescript": "^12.1.2",
20
20
  "@types/js-cookie": "^3.0.6",
21
- "@types/node": "^22.13.1",
21
+ "@types/node": "^22.14.1",
22
22
  "commander": "^13.1.0",
23
- "rollup": "^4.34.5",
24
- "tsc-alias": "^1.8.10",
25
- "typescript": "^5.7.3"
23
+ "rollup": "^4.40.0",
24
+ "tsc-alias": "^1.8.15",
25
+ "typescript": "^5.8.3"
26
26
  },
27
27
  "dependencies": {
28
28
  "js-cookie": "^3.0.5",
@@ -3,8 +3,8 @@
3
3
  * Les promesses sont produites par la fonction `fetcher` qui prend un paramètre de type `P`
4
4
  * Chaque nouvelle promesse est demandée à `fetcher` dès que précédente est résolue.
5
5
  *
6
- * @param params le tableau des paramètres à passer à `fetcher`
6
+ * @param params un iterable des paramètres à passer à `fetcher`
7
7
  * @param fetcher une fonction qui prend un paramètre et retourne une promesse d'objet de type `T`
8
8
  * @returns un iterable de tuples (param, promesse)
9
9
  */
10
- export default function <P, T>(params: P[], fetcher: (param: P) => Promise<T>): Iterable<[P, Promise<T>]>;
10
+ export default function <P, T>(params: Iterable<P>, fetcher: (param: P) => Promise<T>): Iterable<[P, Promise<T>]>;
package/util/iterator.js CHANGED
@@ -1,44 +1,65 @@
1
+ function noop() { }
1
2
  /**
2
3
  * Fournit un itérateur qui produit des promesses d'objets de type `T`
3
4
  * Les promesses sont produites par la fonction `fetcher` qui prend un paramètre de type `P`
4
5
  * Chaque nouvelle promesse est demandée à `fetcher` dès que précédente est résolue.
5
6
  *
6
- * @param params le tableau des paramètres à passer à `fetcher`
7
+ * @param params un iterable des paramètres à passer à `fetcher`
7
8
  * @param fetcher une fonction qui prend un paramètre et retourne une promesse d'objet de type `T`
8
9
  * @returns un iterable de tuples (param, promesse)
9
10
  */
10
11
  export default function (params, fetcher) {
11
12
  return (function* () {
12
- const len = params.length;
13
- if (len === 0)
14
- return;
13
+ const iterator = params[Symbol.iterator]();
15
14
  let nextParam = null;
16
- let nextYield = null;
17
- function prepareNext(i) {
15
+ let nextPromise = null;
16
+ let more = false;
17
+ function prepareNext() {
18
+ const { done, value } = iterator.next();
19
+ if (done) {
20
+ nextParam = null;
21
+ nextPromise = null;
22
+ more = false;
23
+ return;
24
+ }
25
+ nextParam = value;
18
26
  try {
19
- nextParam = params[i];
20
- nextYield = fetcher(nextParam);
27
+ const np = fetcher(nextParam);
28
+ if (np instanceof Promise) {
29
+ nextPromise = np;
30
+ }
31
+ else {
32
+ nextPromise = Promise.resolve(np);
33
+ }
21
34
  }
22
35
  catch (e) {
23
- nextYield = Promise.reject(e);
36
+ nextPromise = Promise.reject(e);
24
37
  }
38
+ // on ajoute un catch pour éviter UnhandledPromiseRejection
39
+ nextPromise.catch(noop);
40
+ more = true;
25
41
  }
26
- prepareNext(0);
27
- if (len === 1) {
28
- yield [nextParam, nextYield];
29
- return;
30
- }
31
- for (let i = 1; i < len; ++i) {
32
- if (nextYield == null)
33
- throw new Error('Problème d\'itération: la boucle doit attendre une promesse avant de réclamer la suivante.');
42
+ prepareNext();
43
+ while (more) {
44
+ more = false;
34
45
  const paramToYield = nextParam;
35
- const toYield = nextYield;
36
- nextYield = null;
37
- yield [paramToYield, toYield.then(v => { prepareNext(i); return v; }, r => { prepareNext(i); throw r; })];
46
+ nextParam = null;
47
+ let prepared = false;
48
+ const promiseToYield = nextPromise.finally(() => {
49
+ if (!prepared) {
50
+ prepared = true;
51
+ prepareNext();
52
+ }
53
+ });
54
+ nextPromise = null;
55
+ // on ajoute un catch pour éviter UnhandledPromiseRejection
56
+ promiseToYield.catch(noop);
57
+ yield [paramToYield, promiseToYield];
58
+ if (!prepared) {
59
+ prepared = true;
60
+ prepareNext();
61
+ }
38
62
  }
39
- if (nextYield == null)
40
- throw new Error('Problème d\'itération: ');
41
- yield [nextParam, nextYield];
42
63
  })();
43
64
  }
44
65
  //# sourceMappingURL=iterator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"iterator.js","sourceRoot":"","sources":["../src/util/iterator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,WACV,MAAW,EAAE,OAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAC;QACb,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;QAC1B,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO;QACtB,IAAI,SAAS,GAAa,IAAI,CAAC;QAC/B,IAAI,SAAS,GAAsB,IAAI,CAAC;QACxC,SAAS,WAAW,CAAC,CAAS;YAC1B,IAAI,CAAC;gBACD,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBACtB,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAClC,CAAC;QACL,CAAC;QACD,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;YACZ,MAAM,CAAC,SAAe,EAAE,SAAwB,CAAC,CAAC;YAClD,OAAO;QACX,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;YAC3B,IAAI,SAAS,IAAI,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC;YACrI,MAAM,YAAY,GAAG,SAAc,CAAC;YACpC,MAAM,OAAO,GAAG,SAAuB,CAAC;YACxC,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,CAC7B,CAAC,CAAC,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA,CAAC,CAAC,EACjC,CAAC,CAAC,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA,CAAC,CAAC,CACnC,CAAC,CAAC;QACP,CAAC;QACD,IAAI,SAAS,IAAI,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;QACjE,MAAM,CAAC,SAAe,EAAE,SAAwB,CAAC,CAAC;IACtD,CAAC,CAAC,EAAE,CAAC;AACT,CAAC","sourcesContent":["/**\n * Fournit un itérateur qui produit des promesses d'objets de type `T`\n * Les promesses sont produites par la fonction `fetcher` qui prend un paramètre de type `P`\n * Chaque nouvelle promesse est demandée à `fetcher` dès que précédente est résolue.\n * \n * @param params le tableau des paramètres à passer à `fetcher`\n * @param fetcher une fonction qui prend un paramètre et retourne une promesse d'objet de type `T`\n * @returns un iterable de tuples (param, promesse)\n */\nexport default function <P, T>(\n params: P[], fetcher: (param: P) => Promise<T>): Iterable<[P, Promise<T>]> {\n return (function* () {\n const len = params.length;\n if (len === 0) return;\n let nextParam: P | null = null;\n let nextYield: Promise<T> | null = null;\n function prepareNext(i: number): void {\n try {\n nextParam = params[i];\n nextYield = fetcher(nextParam);\n } catch (e) {\n nextYield = Promise.reject(e);\n }\n }\n prepareNext(0);\n if (len === 1) {\n yield [nextParam! as P, nextYield! as Promise<T>];\n return;\n }\n for (let i = 1; i < len; ++i) {\n if (nextYield == null) throw new Error('Problème d\\'itération: la boucle doit attendre une promesse avant de réclamer la suivante.');\n const paramToYield = nextParam as P;\n const toYield = nextYield as Promise<T>;\n nextYield = null;\n yield [paramToYield, toYield.then(\n v => { prepareNext(i); return v },\n r => { prepareNext(i); throw r }\n )];\n }\n if (nextYield == null) throw new Error('Problème d\\'itération: ')\n yield [nextParam! as P, nextYield! as Promise<T>];\n })();\n}\n\n"]}
1
+ {"version":3,"file":"iterator.js","sourceRoot":"","sources":["../src/util/iterator.ts"],"names":[],"mappings":"AAAA,SAAS,IAAI,KAAK,CAAC;AAEnB;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,WACV,MAAmB,EAAE,OAAiC;IACtD,OAAO,CAAC,QAAQ,CAAC;QACb,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,IAAI,SAAS,GAAa,IAAI,CAAC;QAC/B,IAAI,WAAW,GAAsB,IAAI,CAAC;QAC1C,IAAI,IAAI,GAAY,KAAK,CAAC;QAC1B,SAAS,WAAW;YAChB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,IAAI,EAAE,CAAC;gBACP,SAAS,GAAG,IAAI,CAAC;gBACjB,WAAW,GAAG,IAAI,CAAC;gBACnB,IAAI,GAAG,KAAK,CAAC;gBACb,OAAO;YACX,CAAC;YACD,SAAS,GAAG,KAAK,CAAC;YAClB,IAAI,CAAC;gBACD,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC9B,IAAI,EAAE,YAAY,OAAO,EAAE,CAAC;oBACxB,WAAW,GAAG,EAAE,CAAC;gBACrB,CAAC;qBAAM,CAAC;oBACJ,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACtC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpC,CAAC;YACD,2DAA2D;YAC3D,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,WAAW,EAAE,CAAC;QACd,OAAO,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,KAAK,CAAC;YACb,MAAM,YAAY,GAAG,SAAc,CAAC;YACpC,SAAS,GAAG,IAAI,CAAC;YACjB,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,MAAM,cAAc,GAAI,WAA2B,CAAC,OAAO,CACvD,GAAG,EAAE;gBACD,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACZ,QAAQ,GAAG,IAAI,CAAC;oBAChB,WAAW,EAAE,CAAC;gBAClB,CAAC;YACL,CAAC,CACJ,CAAC;YACF,WAAW,GAAG,IAAI,CAAC;YACnB,2DAA2D;YAC3D,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;YACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACZ,QAAQ,GAAG,IAAI,CAAC;gBAChB,WAAW,EAAE,CAAC;YAClB,CAAC;QACL,CAAC;IACL,CAAC,CAAC,EAAE,CAAC;AACT,CAAC","sourcesContent":["function noop() { }\n\n/**\n * Fournit un itérateur qui produit des promesses d'objets de type `T`\n * Les promesses sont produites par la fonction `fetcher` qui prend un paramètre de type `P`\n * Chaque nouvelle promesse est demandée à `fetcher` dès que précédente est résolue.\n * \n * @param params un iterable des paramètres à passer à `fetcher`\n * @param fetcher une fonction qui prend un paramètre et retourne une promesse d'objet de type `T`\n * @returns un iterable de tuples (param, promesse)\n */\nexport default function <P, T>(\n params: Iterable<P>, fetcher: (param: P) => Promise<T>): Iterable<[P, Promise<T>]> {\n return (function* () {\n const iterator = params[Symbol.iterator]();\n let nextParam: P | null = null;\n let nextPromise: Promise<T> | null = null;\n let more: boolean = false;\n function prepareNext(): void {\n const { done, value } = iterator.next();\n if (done) {\n nextParam = null;\n nextPromise = null;\n more = false;\n return;\n }\n nextParam = value;\n try {\n const np = fetcher(nextParam);\n if (np instanceof Promise) {\n nextPromise = np;\n } else {\n nextPromise = Promise.resolve(np);\n }\n } catch (e) {\n nextPromise = Promise.reject(e);\n }\n // on ajoute un catch pour éviter UnhandledPromiseRejection\n nextPromise.catch(noop);\n more = true;\n }\n prepareNext();\n while (more) {\n more = false;\n const paramToYield = nextParam as P;\n nextParam = null;\n let prepared = false;\n const promiseToYield = (nextPromise! as Promise<T>).finally(\n () => {\n if (!prepared) {\n prepared = true;\n prepareNext();\n }\n }\n );\n nextPromise = null;\n // on ajoute un catch pour éviter UnhandledPromiseRejection\n promiseToYield.catch(noop);\n yield [paramToYield, promiseToYield];\n if (!prepared) {\n prepared = true;\n prepareNext();\n }\n }\n })();\n}\n\n"]}