@cyc-seattle/platform 1.38.0 → 1.38.1

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.
@@ -32,7 +32,10 @@ export function scheduleActivities(ctx) {
32
32
  const span = start !== null && end !== null ? end - start : 0;
33
33
  const closedFor = closed.get(activity.date);
34
34
  if (closedFor !== undefined && activity.kind !== "travel") {
35
- ctx.warn("closed_date", `${offering.name} has an activity on ${activity.date}, a closed day: ${closedFor}`, activity.key, { version_id: activity.version_id, date: activity.date });
35
+ ctx.warn("closed_date", `${offering.name}'s ${activity.kind} falls on ${closedFor}, a closed day`, activity.key, {
36
+ version_id: activity.version_id,
37
+ date: activity.date,
38
+ });
36
39
  }
37
40
  const usesBoats = activity.kind !== "travel" && activity.uses_boats;
38
41
  result.push({
@@ -10,6 +10,22 @@ function sum(values) {
10
10
  total += v;
11
11
  return total;
12
12
  }
13
+ /**
14
+ * `n` of something, in words: "9 Sailqubes", "1 Opti". Only a one-word name is made plural, and not one that already
15
+ * ends in "s", such as RIBs; any other name is used as given, since adding an "s" to "Laser Radial" reads wrong.
16
+ */
17
+ function countOf(n, name) {
18
+ const plural = n !== 1 && /^\S+$/.test(name) && !/s$/i.test(name);
19
+ return `${n} ${plural ? `${name}s` : name}`;
20
+ }
21
+ /** Names as a sentence lists them: `a`, `a and b`, or `a, b, and c`. */
22
+ function listOf(names) {
23
+ return names.length <= 2 ? names.join(" and ") : `${names.slice(0, -1).join(", ")}, and ${names.at(-1)}`;
24
+ }
25
+ /** The offerings a block holds, by name, so a warning about it says whose it is. */
26
+ function offeringsOf(block) {
27
+ return listOf([...new Set(block.members.map((a) => a.offering.name))]);
28
+ }
13
29
  function groupBy(rows, key) {
14
30
  const groups = new Map();
15
31
  for (const row of rows)
@@ -71,7 +87,7 @@ export class Staffing {
71
87
  continue;
72
88
  }
73
89
  if (row.date !== a.date) {
74
- this.ctx.warn("block_date_mismatch", `${a.offering.name}'s activity on ${a.date} is in ${row.name}, which is on ${row.date}`, a.stored.key, { version_id: a.stored.version_id, date: a.date });
90
+ this.ctx.warn("block_date_mismatch", `${a.offering.name}'s ${a.kind} is in ${row.name}, which is on ${row.date}`, a.stored.key, { version_id: a.stored.version_id, date: a.date });
75
91
  }
76
92
  members.set(id, [...(members.get(id) ?? []), a]);
77
93
  }
@@ -114,7 +130,7 @@ export class Staffing {
114
130
  const block = a.stored.block_id == null ? undefined : blocks.get(a.stored.block_id);
115
131
  if (block !== undefined && sum(block.staff.map((s) => s.count)) > 0)
116
132
  continue;
117
- this.ctx.warn("unallocated_activity", `${a.offering.name}'s ${a.kind} on ${a.date} has no staff`, a.stored.key, {
133
+ this.ctx.warn("unallocated_activity", `${a.offering.name}'s ${a.kind} has no staff`, a.stored.key, {
118
134
  version_id: a.stored.version_id,
119
135
  date: a.date,
120
136
  });
@@ -146,7 +162,7 @@ export class Staffing {
146
162
  if (sailors <= 0)
147
163
  return;
148
164
  const scope = { version_id: block.row.version_id, date: block.row.date };
149
- const where = `${block.row.date}, ${block.row.name}`;
165
+ const where = offeringsOf(block);
150
166
  let load = 0;
151
167
  let counted = 0;
152
168
  const required = [];
@@ -172,7 +188,7 @@ export class Staffing {
172
188
  return required.some((r) => this.qualifiedFor(held, level, r)) ? s.count : 0;
173
189
  }));
174
190
  if (load > qualified + 1e-9) {
175
- ctx.warn("over_capacity", `${where}: ${counted} sailors, but its staff can take ${Math.floor((qualified * counted) / load + 1e-9)}`, block.row.key, scope);
191
+ ctx.warn("over_capacity", `${where}: ${counted} sailors, but the staff can take ${Math.floor((qualified * counted) / load + 1e-9)}`, block.row.key, scope);
176
192
  }
177
193
  }
178
194
  if (!block.members.some((a) => a.uses_boats))
@@ -188,7 +204,7 @@ export class Staffing {
188
204
  seats += (b.count + (b.sailor_owned ?? 0)) * perBoat;
189
205
  }
190
206
  if (sailors > seats) {
191
- ctx.warn("over_capacity", `${where}: ${sailors} sailors, but its boats can take ${seats}`, block.row.key, scope);
207
+ ctx.warn("over_capacity", `${where}: ${sailors} sailors, but the boats can take ${seats}`, block.row.key, scope);
192
208
  }
193
209
  }
194
210
  /**
@@ -201,6 +217,7 @@ export class Staffing {
201
217
  const classes = new Map(ctx.data.boat_classes.map((c) => [c.id, c]));
202
218
  const fleet = new Map(ctx.data.plan_boat_rates.map((r) => [r.boat_class_id, r.quantity]));
203
219
  const resources = new Map(ctx.data.plan_resources.map((r) => [r.key, r]));
220
+ const organization = ctx.data.organization.name;
204
221
  for (const [date, list] of dates) {
205
222
  const check = (code, what, have, used) => {
206
223
  if (have == null)
@@ -209,9 +226,12 @@ export class Staffing {
209
226
  const peak = mostAtOnce(out);
210
227
  if (peak.total <= have)
211
228
  return;
212
- const names = peak.at.map(({ block, n }) => `${block.row.name} ${n}`).join(", ");
229
+ const whose = peak.at.length === 1
230
+ ? `, for ${offeringsOf(peak.at[0].block)}`
231
+ : `: ${listOf(peak.at.map(({ block, n }) => `${n} for ${offeringsOf(block)}`))}`;
232
+ const message = `${countOf(peak.total, what)} out, but ${organization} has ${have}${whose}`;
213
233
  for (const version of new Set(peak.at.map(({ block }) => block.row.version_id))) {
214
- ctx.warn(code, `${date}: ${peak.total} ${what} out, but the organization has ${have} (${names})`, what, {
234
+ ctx.warn(code, message, what, {
215
235
  version_id: version,
216
236
  date: date,
217
237
  });
@@ -258,7 +278,7 @@ export class Staffing {
258
278
  const total = result.get(key) ?? { hours: 0, dates: 0 };
259
279
  result.set(key, { hours: total.hours + hours, dates: total.dates + 1 });
260
280
  for (const version of new Set(list.map((b) => b.row.version_id))) {
261
- this.ctx.warn("position_double_booked", `${names.get(key) ?? key} coaches ${clash[0].row.name} and ${clash[1].row.name} at the same time on ${date}` +
281
+ this.ctx.warn("position_double_booked", `${names.get(key) ?? key} coaches ${clash[0].row.name} and ${clash[1].row.name} at the same time` +
262
282
  (hours > 0 ? `, so another coach is needed for ${round2(hours)} hours` : ""), key, { version_id: version, date });
263
283
  }
264
284
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyc-seattle/platform",
3
- "version": "1.38.0",
3
+ "version": "1.38.1",
4
4
  "description": "Pulumi components for CYC's shared Google Cloud platform: WebService, the platform outputs reader, and the stacks that build the platform",
5
5
  "author": "CYC Community Sailing Center",
6
6
  "license": "Apache-2.0",