@olinfo/quizms-firebase 4.1.2 → 5.0.0
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.
- package/dist/cli.js +14 -14
- package/dist/cli.js.map +1 -1
- package/dist/package.json.js +1 -1
- package/dist/src/cli/definalize.js +17 -17
- package/dist/src/cli/definalize.js.map +1 -1
- package/dist/src/cli/export.js +42 -34
- package/dist/src/cli/export.js.map +1 -1
- package/dist/src/cli/files/firestore-indexes.json.js +1 -1
- package/dist/src/cli/files/firestore.rules.js +27 -27
- package/dist/src/cli/files/firestore.rules.js.map +1 -1
- package/dist/src/cli/files/functions-index.js +3 -3
- package/dist/src/cli/files/functions-index.js.map +1 -1
- package/dist/src/cli/files/storage.rules.js +3 -3
- package/dist/src/cli/files/storage.rules.js.map +1 -1
- package/dist/src/cli/import.js +65 -71
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/update-scores.js +7 -7
- package/dist/src/cli/update-scores.js.map +1 -1
- package/dist/src/cli/utils/converters-admin.js +23 -22
- package/dist/src/cli/utils/converters-admin.js.map +1 -1
- package/dist/src/functions/api/index.js +18 -18
- package/dist/src/functions/api/index.js.map +1 -1
- package/dist/src/functions/api/queries.js +21 -21
- package/dist/src/functions/api/queries.js.map +1 -1
- package/dist/src/functions/api/student.js +46 -47
- package/dist/src/functions/api/student.js.map +1 -1
- package/dist/src/functions/api/teacher.js +92 -95
- package/dist/src/functions/api/teacher.js.map +1 -1
- package/dist/src/functions/submissions.js +11 -7
- package/dist/src/functions/submissions.js.map +1 -1
- package/dist/src/functions/update-scores.js +14 -14
- package/dist/src/functions/update-scores.js.map +1 -1
- package/dist/src/models/submission.js +13 -0
- package/dist/src/models/submission.js.map +1 -0
- package/dist/src/web/admin/announcements.js +45 -42
- package/dist/src/web/admin/announcements.js.map +1 -1
- package/dist/src/web/admin/contest-settings.js +19 -11
- package/dist/src/web/admin/contest-settings.js.map +1 -1
- package/dist/src/web/admin/context.js.map +1 -1
- package/dist/src/web/admin/dashboard.js +56 -35
- package/dist/src/web/admin/dashboard.js.map +1 -1
- package/dist/src/web/admin/export.js +100 -43
- package/dist/src/web/admin/export.js.map +1 -1
- package/dist/src/web/admin/layout.js +25 -21
- package/dist/src/web/admin/layout.js.map +1 -1
- package/dist/src/web/admin/provider.js +41 -21
- package/dist/src/web/admin/provider.js.map +1 -1
- package/dist/src/web/admin/school-table.js +20 -24
- package/dist/src/web/admin/school-table.js.map +1 -1
- package/dist/src/web/common/api.js +24 -20
- package/dist/src/web/common/api.js.map +1 -1
- package/dist/src/web/common/converters.js +38 -39
- package/dist/src/web/common/converters.js.map +1 -1
- package/dist/src/web/common/token-login.js +18 -18
- package/dist/src/web/common/token-login.js.map +1 -1
- package/dist/src/web/entry/admin.js +22 -13
- package/dist/src/web/entry/admin.js.map +1 -1
- package/dist/src/web/entry/student-statement.js +1 -1
- package/dist/src/web/entry/student-statement.js.map +1 -1
- package/dist/src/web/entry/student.js +80 -75
- package/dist/src/web/entry/student.js.map +1 -1
- package/dist/src/web/entry/teacher.js +63 -62
- package/dist/src/web/entry/teacher.js.map +1 -1
- package/package.json +13 -13
package/dist/src/cli/import.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { existsSync as
|
|
2
|
-
import { readFile as
|
|
1
|
+
import { existsSync as b } from "node:fs";
|
|
2
|
+
import { readFile as I } from "node:fs/promises";
|
|
3
3
|
import m from "node:path";
|
|
4
|
-
import { contestSchema as $,
|
|
5
|
-
import { Rng as
|
|
6
|
-
import { fatal as
|
|
4
|
+
import { contestSchema as $, venueSchema as g, studentSchema as C, variantsConfigSchema as j, variantSchema as V } from "@olinfo/quizms/models";
|
|
5
|
+
import { Rng as x, validate as z } from "@olinfo/quizms/utils";
|
|
6
|
+
import { fatal as w, warning as M, success as P, load as f, loadContests as v } from "@olinfo/quizms/utils-node";
|
|
7
7
|
import { deleteApp as D } from "firebase-admin/app";
|
|
8
|
-
import { groupBy as
|
|
9
|
-
import
|
|
10
|
-
import { glob as
|
|
11
|
-
import
|
|
12
|
-
import { userSchema as
|
|
8
|
+
import { groupBy as A, uniq as y } from "lodash-es";
|
|
9
|
+
import N from "picomatch";
|
|
10
|
+
import { glob as O } from "tinyglobby";
|
|
11
|
+
import u from "zod";
|
|
12
|
+
import { userSchema as U } from "../models/user.js";
|
|
13
13
|
import { websiteSchema as k } from "../models/website.js";
|
|
14
|
-
import { importCollection as
|
|
15
|
-
import { contestConverter as q,
|
|
16
|
-
import { initializeFirebase as
|
|
17
|
-
import { importStorage as
|
|
14
|
+
import { importCollection as p } from "./utils/collection.js";
|
|
15
|
+
import { contestConverter as q, venueConverter as F, studentConverter as B, variantConverter as J, websiteConverter as Q } from "./utils/converters-admin.js";
|
|
16
|
+
import { initializeFirebase as R } from "./utils/initialize.js";
|
|
17
|
+
import { importStorage as W } from "./utils/storage.js";
|
|
18
18
|
import { importUsers as S } from "./utils/users.js";
|
|
19
19
|
async function ht(t) {
|
|
20
|
-
if (
|
|
20
|
+
if (b("data") || w("Cannot find data directory. Make sure you're inside a QuizMS project."), [
|
|
21
21
|
"admins",
|
|
22
22
|
"contests",
|
|
23
23
|
"schools",
|
|
@@ -26,44 +26,44 @@ async function ht(t) {
|
|
|
26
26
|
"teachers",
|
|
27
27
|
"variants",
|
|
28
28
|
"websites"
|
|
29
|
-
].every((
|
|
30
|
-
|
|
29
|
+
].every((i) => !t[i])) {
|
|
30
|
+
M("`Nothing to import. Use `--help` for usage.`");
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
-
const { app:
|
|
34
|
-
t.admins && await
|
|
33
|
+
const { app: n, bucket: o, db: s } = await R();
|
|
34
|
+
t.admins && await E(s, t), t.contests && await G(s, t), (t.schools || t.teachers) && await H(s, t), t.students && await K(s, t), t.variants && await L(s, t), t.websites && await T(s, t), t.statements && await X(o, t), P("All done!"), await D(n);
|
|
35
35
|
}
|
|
36
|
-
async function
|
|
37
|
-
const
|
|
38
|
-
await S(t, "admin",
|
|
36
|
+
async function E(t, e) {
|
|
37
|
+
const n = await f("admins", U.omit({ role: !0 }));
|
|
38
|
+
await S(t, "admin", n, e);
|
|
39
39
|
}
|
|
40
|
-
async function
|
|
41
|
-
const
|
|
42
|
-
await
|
|
40
|
+
async function G(t, e) {
|
|
41
|
+
const n = await f("contests", $);
|
|
42
|
+
await p(t, "contests", n, q, e);
|
|
43
43
|
}
|
|
44
|
-
async function
|
|
45
|
-
const
|
|
44
|
+
async function H(t, e) {
|
|
45
|
+
const n = g.omit({
|
|
46
46
|
schoolId: !0,
|
|
47
47
|
contestId: !0
|
|
48
48
|
}).extend({
|
|
49
|
-
contestIds:
|
|
50
|
-
password:
|
|
51
|
-
}), o = await f("schools",
|
|
52
|
-
if (
|
|
53
|
-
const
|
|
49
|
+
contestIds: u.union([u.string(), u.array(u.string())]).default("*"),
|
|
50
|
+
password: u.string()
|
|
51
|
+
}), o = await f("schools", n), s = await v();
|
|
52
|
+
if (e.teachers) {
|
|
53
|
+
const i = o.map((a) => ({
|
|
54
54
|
name: a.name,
|
|
55
55
|
username: a.id,
|
|
56
56
|
password: a.password
|
|
57
57
|
}));
|
|
58
|
-
await S(t, "teacher",
|
|
58
|
+
await S(t, "teacher", i, e);
|
|
59
59
|
}
|
|
60
|
-
if (
|
|
61
|
-
const
|
|
62
|
-
for (const a of
|
|
60
|
+
if (e.schools) {
|
|
61
|
+
const i = [];
|
|
62
|
+
for (const a of s)
|
|
63
63
|
for (const r of o) {
|
|
64
|
-
if (!
|
|
65
|
-
let c;
|
|
66
|
-
a.hasVariants && (r.pdfVariants ? c = r.pdfVariants.map((d) => `${a.id}-${d}`) : c = new
|
|
64
|
+
if (!N.isMatch(a.id, r.contestIds)) continue;
|
|
65
|
+
let c = [];
|
|
66
|
+
a.hasVariants && (r.pdfVariants ? c = r.pdfVariants.map((d) => `${a.id}-${d}`) : c = new x(`${a.secret}-${a.id}-${r.id}-venue`).sample(a.pdfVariantIds, a.pdfPerSchool)), i.push({
|
|
67
67
|
id: `${r.id}-${a.id}`,
|
|
68
68
|
schoolId: r.id,
|
|
69
69
|
contestId: a.id,
|
|
@@ -73,66 +73,60 @@ async function _(t, s) {
|
|
|
73
73
|
disabled: !1
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
|
-
await
|
|
76
|
+
await p(t, "venues", i, F, e);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
async function
|
|
80
|
-
const
|
|
79
|
+
async function K(t, e) {
|
|
80
|
+
const n = await f("students", C), o = A(n, "venueId");
|
|
81
81
|
await Promise.all(
|
|
82
82
|
Object.entries(o).map(
|
|
83
|
-
([
|
|
84
|
-
t,
|
|
85
|
-
`participations/${i}/students`,
|
|
86
|
-
n,
|
|
87
|
-
Q,
|
|
88
|
-
s
|
|
89
|
-
)
|
|
83
|
+
([s, i]) => p(t, `venues/${s}/students`, i, B, e)
|
|
90
84
|
)
|
|
91
85
|
);
|
|
92
86
|
}
|
|
93
|
-
async function
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
const r = m.join("variants",
|
|
87
|
+
async function L(t, e) {
|
|
88
|
+
const n = await f("variants", j), o = await Promise.all(
|
|
89
|
+
n.flatMap((s) => y([...s.variantIds, ...s.pdfVariantIds]).map(async (a) => {
|
|
90
|
+
const r = m.join("variants", s.id, a, "answers.json");
|
|
97
91
|
let c;
|
|
98
92
|
try {
|
|
99
|
-
c = await
|
|
93
|
+
c = await I(r, "utf8");
|
|
100
94
|
} catch {
|
|
101
|
-
|
|
95
|
+
w(`Cannot find schema for variant ${a}. Use \`quizms variants\` to generate it.`);
|
|
102
96
|
}
|
|
103
97
|
try {
|
|
104
|
-
return
|
|
98
|
+
return z(V, JSON.parse(c));
|
|
105
99
|
} catch (d) {
|
|
106
|
-
|
|
100
|
+
w(`Invalid schema for variant ${a}: ${d}`);
|
|
107
101
|
}
|
|
108
102
|
}))
|
|
109
103
|
);
|
|
110
|
-
await
|
|
104
|
+
await p(t, "variants", o, J, e);
|
|
111
105
|
}
|
|
112
|
-
async function
|
|
113
|
-
const
|
|
114
|
-
await
|
|
106
|
+
async function T(t, e) {
|
|
107
|
+
const n = await f("websites", k);
|
|
108
|
+
await p(t, "websites", n, Q, e);
|
|
115
109
|
}
|
|
116
|
-
async function
|
|
117
|
-
const
|
|
118
|
-
const a = y([...
|
|
110
|
+
async function X(t, e) {
|
|
111
|
+
const n = await v(), o = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-"), s = n.map(async (i) => {
|
|
112
|
+
const a = y([...i.variantIds, ...i.pdfVariantIds]).map(
|
|
119
113
|
async (r) => {
|
|
120
|
-
const c = m.join("variants",
|
|
121
|
-
return (await
|
|
122
|
-
const h = m.extname(
|
|
114
|
+
const c = m.join("variants", i.id, r), d = m.join("statements", i.id, r);
|
|
115
|
+
return (await O("*.{pdf,js,css}", { cwd: c })).flatMap((l) => {
|
|
116
|
+
const h = m.extname(l);
|
|
123
117
|
return [
|
|
124
118
|
[
|
|
125
|
-
m.join(c,
|
|
126
|
-
m.join(d, `${m.basename(
|
|
119
|
+
m.join(c, l),
|
|
120
|
+
m.join(d, `${m.basename(l, h)}-${o}${h}`)
|
|
127
121
|
],
|
|
128
|
-
[m.join(c,
|
|
122
|
+
[m.join(c, l), m.join(d, l)]
|
|
129
123
|
];
|
|
130
124
|
});
|
|
131
125
|
}
|
|
132
126
|
);
|
|
133
127
|
return (await Promise.all(a)).flat();
|
|
134
128
|
});
|
|
135
|
-
await
|
|
129
|
+
await W(t, "statements", (await Promise.all(s)).flat(), e);
|
|
136
130
|
}
|
|
137
131
|
export {
|
|
138
132
|
ht as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.js","sources":["../../../src/cli/import.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport {\n contestSchema,\n type Participation,\n participationSchema,\n studentSchema,\n variantSchema,\n variantsConfigSchema,\n} from \"@olinfo/quizms/models\";\nimport { Rng, validate } from \"@olinfo/quizms/utils\";\nimport { fatal, load, loadContests, success, warning } from \"@olinfo/quizms/utils-node\";\nimport { deleteApp } from \"firebase-admin/app\";\nimport type { Firestore } from \"firebase-admin/firestore\";\nimport { groupBy, uniq } from \"lodash-es\";\nimport picomatch from \"picomatch\";\nimport { glob } from \"tinyglobby\";\nimport z from \"zod\";\n\nimport { userSchema } from \"~/models/user\";\nimport { websiteSchema } from \"~/models/website\";\n\nimport { importCollection } from \"./utils/collection\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n variantConverter,\n websiteConverter,\n} from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\nimport { importStorage } from \"./utils/storage\";\nimport { importUsers } from \"./utils/users\";\n\ntype ImportOptions = {\n config: string;\n skipExisting?: true;\n delete?: true;\n force?: true;\n\n admins?: true;\n contests?: true;\n schools?: true;\n statements?: true;\n students?: true;\n teachers?: true;\n variants?: true;\n websites?: true;\n};\n\nexport default async function importData(options: ImportOptions) {\n process.env.QUIZMS_MODE = \"contest\";\n\n if (!existsSync(\"data\")) {\n fatal(\"Cannot find data directory. Make sure you're inside a QuizMS project.\");\n }\n\n const collections: (keyof ImportOptions)[] = [\n \"admins\",\n \"contests\",\n \"schools\",\n \"statements\",\n \"students\",\n \"teachers\",\n \"variants\",\n \"websites\",\n ];\n if (collections.every((key) => !options[key])) {\n warning(\"`Nothing to import. Use `--help` for usage.`\");\n return;\n }\n\n const { app, bucket, db } = await initializeFirebase();\n\n if (options.admins) {\n await importAdmins(db, options);\n }\n if (options.contests) {\n await importContests(db, options);\n }\n if (options.schools || options.teachers) {\n await importParticipations(db, options);\n }\n if (options.students) {\n await importStudents(db, options);\n }\n if (options.variants) {\n await importVariants(db, options);\n }\n if (options.websites) {\n await importWebsites(db, options);\n }\n if (options.statements) {\n await importStatements(bucket, options);\n }\n\n success(\"All done!\");\n await deleteApp(app);\n}\n\nasync function importAdmins(db: Firestore, options: ImportOptions) {\n const users = await load(\"admins\", userSchema.omit({ role: true }));\n await importUsers(db, \"admin\", users, options);\n}\n\nasync function importContests(db: Firestore, options: ImportOptions) {\n const contests = await load(\"contests\", contestSchema);\n await importCollection(db, \"contests\", contests, contestConverter, options);\n}\n\nasync function importParticipations(db: Firestore, options: ImportOptions) {\n const schoolSchema = participationSchema\n .omit({\n schoolId: true,\n contestId: true,\n })\n .extend({\n contestIds: z.union([z.string(), z.array(z.string())]).default(\"*\"),\n password: z.string(),\n });\n const schools = await load(\"schools\", schoolSchema);\n const contests = await loadContests();\n\n if (options.teachers) {\n const teachers = schools.map((school) => ({\n name: school.name,\n username: school.id,\n password: school.password,\n }));\n await importUsers(db, \"teacher\", teachers, options);\n }\n\n if (options.schools) {\n const participations: Participation[] = [];\n\n for (const contest of contests) {\n for (const school of schools) {\n if (!picomatch.isMatch(contest.id, school.contestIds)) continue;\n\n let pdfVariants: string[] | undefined;\n if (contest.hasVariants) {\n if (school.pdfVariants) {\n pdfVariants = school.pdfVariants.map((id) => `${contest.id}-${id}`);\n } else {\n const rng = new Rng(`${contest.secret}-${contest.id}-${school.id}-participation`);\n pdfVariants = rng.sample(contest.pdfVariantIds, contest.pdfPerSchool);\n }\n }\n\n participations.push({\n id: `${school.id}-${contest.id}`,\n schoolId: school.id,\n contestId: contest.id,\n name: school.name,\n finalized: false,\n pdfVariants,\n disabled: false,\n });\n }\n }\n\n await importCollection(db, \"participations\", participations, participationConverter, options);\n }\n}\n\nasync function importStudents(db: Firestore, options: ImportOptions) {\n const students = await load(\"students\", studentSchema);\n const participations = groupBy(students, \"participationId\");\n await Promise.all(\n Object.entries(participations).map(([participationId, students]) =>\n importCollection(\n db,\n `participations/${participationId}/students`,\n students,\n studentConverter,\n options,\n ),\n ),\n );\n}\n\nasync function importVariants(db: Firestore, options: ImportOptions) {\n const variantsConfig = await load(\"variants\", variantsConfigSchema);\n const variants = await Promise.all(\n variantsConfig.flatMap((config) => {\n const ids = uniq([...config.variantIds, ...config.pdfVariantIds]);\n return ids.map(async (id) => {\n const fileName = path.join(\"variants\", config.id, id, \"answers.json\");\n let schema: string;\n try {\n schema = await readFile(fileName, \"utf8\");\n } catch {\n fatal(`Cannot find schema for variant ${id}. Use \\`quizms variants\\` to generate it.`);\n }\n try {\n return validate(variantSchema, JSON.parse(schema));\n } catch (err) {\n fatal(`Invalid schema for variant ${id}: ${err}`);\n }\n });\n }),\n );\n await importCollection(db, \"variants\", variants, variantConverter, options);\n}\n\nasync function importWebsites(db: Firestore, options: ImportOptions) {\n const websites = await load(\"websites\", websiteSchema);\n await importCollection(db, \"websites\", websites, websiteConverter, options);\n}\n\nasync function importStatements(bucket: Bucket, options: ImportOptions) {\n const contests = await loadContests();\n\n const timestamp = new Date().toISOString().replace(/:/g, \"-\");\n\n const statements = contests.map(async (config) => {\n const files = uniq([...config.variantIds, ...config.pdfVariantIds]).map(\n async (id): Promise<[string, string][]> => {\n const localDir = path.join(\"variants\", config.id, id);\n const remoteDir = path.join(\"statements\", config.id, id);\n const files = await glob(\"*\", { cwd: localDir });\n\n return files.flatMap((file) => {\n const ext = path.extname(file);\n return [\n [\n path.join(localDir, file),\n path.join(remoteDir, `${path.basename(file, ext)}-${timestamp}${ext}`),\n ],\n [path.join(localDir, file), path.join(remoteDir, file)],\n ];\n });\n },\n );\n return (await Promise.all(files)).flat();\n });\n await importStorage(bucket, \"statements\", (await Promise.all(statements)).flat(), options);\n}\n"],"names":["importData","options","existsSync","fatal","key","warning","app","bucket","db","initializeFirebase","importAdmins","importContests","importParticipations","importStudents","importVariants","importWebsites","importStatements","success","deleteApp","users","load","userSchema","importUsers","contests","contestSchema","importCollection","contestConverter","schoolSchema","participationSchema","z","schools","loadContests","teachers","school","participations","contest","picomatch","pdfVariants","id","Rng","participationConverter","students","studentSchema","groupBy","participationId","studentConverter","variantsConfig","variantsConfigSchema","variants","config","uniq","fileName","path","schema","readFile","validate","variantSchema","err","variantConverter","websites","websiteSchema","websiteConverter","timestamp","statements","files","localDir","remoteDir","glob","file","ext","importStorage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"import.js","sources":["../../../src/cli/import.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport {\n contestSchema,\n studentSchema,\n type Venue,\n variantSchema,\n variantsConfigSchema,\n venueSchema,\n} from \"@olinfo/quizms/models\";\nimport { Rng, validate } from \"@olinfo/quizms/utils\";\nimport { fatal, load, loadContests, success, warning } from \"@olinfo/quizms/utils-node\";\nimport { deleteApp } from \"firebase-admin/app\";\nimport type { Firestore } from \"firebase-admin/firestore\";\nimport { groupBy, uniq } from \"lodash-es\";\nimport picomatch from \"picomatch\";\nimport { glob } from \"tinyglobby\";\nimport z from \"zod\";\n\nimport { userSchema } from \"~/models/user\";\nimport { websiteSchema } from \"~/models/website\";\n\nimport { importCollection } from \"./utils/collection\";\nimport {\n contestConverter,\n studentConverter,\n variantConverter,\n venueConverter,\n websiteConverter,\n} from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\nimport { importStorage } from \"./utils/storage\";\nimport { importUsers } from \"./utils/users\";\n\ntype ImportOptions = {\n config: string;\n skipExisting?: true;\n delete?: true;\n force?: true;\n\n admins?: true;\n contests?: true;\n schools?: true;\n statements?: true;\n students?: true;\n teachers?: true;\n variants?: true;\n websites?: true;\n};\n\nexport default async function importData(options: ImportOptions) {\n if (!existsSync(\"data\")) {\n fatal(\"Cannot find data directory. Make sure you're inside a QuizMS project.\");\n }\n\n const collections: (keyof ImportOptions)[] = [\n \"admins\",\n \"contests\",\n \"schools\",\n \"statements\",\n \"students\",\n \"teachers\",\n \"variants\",\n \"websites\",\n ];\n if (collections.every((key) => !options[key])) {\n warning(\"`Nothing to import. Use `--help` for usage.`\");\n return;\n }\n\n const { app, bucket, db } = await initializeFirebase();\n\n if (options.admins) {\n await importAdmins(db, options);\n }\n if (options.contests) {\n await importContests(db, options);\n }\n if (options.schools || options.teachers) {\n await importVenues(db, options);\n }\n if (options.students) {\n await importStudents(db, options);\n }\n if (options.variants) {\n await importVariants(db, options);\n }\n if (options.websites) {\n await importWebsites(db, options);\n }\n if (options.statements) {\n await importStatements(bucket, options);\n }\n\n success(\"All done!\");\n await deleteApp(app);\n}\n\nasync function importAdmins(db: Firestore, options: ImportOptions) {\n const users = await load(\"admins\", userSchema.omit({ role: true }));\n await importUsers(db, \"admin\", users, options);\n}\n\nasync function importContests(db: Firestore, options: ImportOptions) {\n const contests = await load(\"contests\", contestSchema);\n await importCollection(db, \"contests\", contests, contestConverter, options);\n}\n\nasync function importVenues(db: Firestore, options: ImportOptions) {\n const schoolSchema = venueSchema\n .omit({\n schoolId: true,\n contestId: true,\n })\n .extend({\n contestIds: z.union([z.string(), z.array(z.string())]).default(\"*\"),\n password: z.string(),\n });\n const schools = await load(\"schools\", schoolSchema);\n const contests = await loadContests();\n\n if (options.teachers) {\n const teachers = schools.map((school) => ({\n name: school.name,\n username: school.id,\n password: school.password,\n }));\n await importUsers(db, \"teacher\", teachers, options);\n }\n\n if (options.schools) {\n const venues: Venue[] = [];\n\n for (const contest of contests) {\n for (const school of schools) {\n if (!picomatch.isMatch(contest.id, school.contestIds)) continue;\n\n let pdfVariants: string[] = [];\n if (contest.hasVariants) {\n if (school.pdfVariants) {\n pdfVariants = school.pdfVariants.map((id) => `${contest.id}-${id}`);\n } else {\n const rng = new Rng(`${contest.secret}-${contest.id}-${school.id}-venue`);\n pdfVariants = rng.sample(contest.pdfVariantIds, contest.pdfPerSchool);\n }\n }\n\n venues.push({\n id: `${school.id}-${contest.id}`,\n schoolId: school.id,\n contestId: contest.id,\n name: school.name,\n finalized: false,\n pdfVariants,\n disabled: false,\n });\n }\n }\n\n await importCollection(db, \"venues\", venues, venueConverter, options);\n }\n}\n\nasync function importStudents(db: Firestore, options: ImportOptions) {\n const students = await load(\"students\", studentSchema);\n const venues = groupBy(students, \"venueId\");\n await Promise.all(\n Object.entries(venues).map(([venueId, students]) =>\n importCollection(db, `venues/${venueId}/students`, students, studentConverter, options),\n ),\n );\n}\n\nasync function importVariants(db: Firestore, options: ImportOptions) {\n const variantsConfig = await load(\"variants\", variantsConfigSchema);\n const variants = await Promise.all(\n variantsConfig.flatMap((config) => {\n const ids = uniq([...config.variantIds, ...config.pdfVariantIds]);\n return ids.map(async (id) => {\n const fileName = path.join(\"variants\", config.id, id, \"answers.json\");\n let schema: string;\n try {\n schema = await readFile(fileName, \"utf8\");\n } catch {\n fatal(`Cannot find schema for variant ${id}. Use \\`quizms variants\\` to generate it.`);\n }\n try {\n return validate(variantSchema, JSON.parse(schema));\n } catch (err) {\n fatal(`Invalid schema for variant ${id}: ${err}`);\n }\n });\n }),\n );\n await importCollection(db, \"variants\", variants, variantConverter, options);\n}\n\nasync function importWebsites(db: Firestore, options: ImportOptions) {\n const websites = await load(\"websites\", websiteSchema);\n await importCollection(db, \"websites\", websites, websiteConverter, options);\n}\n\nasync function importStatements(bucket: Bucket, options: ImportOptions) {\n const contests = await loadContests();\n\n const timestamp = new Date().toISOString().replace(/:/g, \"-\");\n\n const statements = contests.map(async (config) => {\n const files = uniq([...config.variantIds, ...config.pdfVariantIds]).map(\n async (id): Promise<[string, string][]> => {\n const localDir = path.join(\"variants\", config.id, id);\n const remoteDir = path.join(\"statements\", config.id, id);\n const files = await glob(\"*.{pdf,js,css}\", { cwd: localDir });\n\n return files.flatMap((file) => {\n const ext = path.extname(file);\n return [\n [\n path.join(localDir, file),\n path.join(remoteDir, `${path.basename(file, ext)}-${timestamp}${ext}`),\n ],\n [path.join(localDir, file), path.join(remoteDir, file)],\n ];\n });\n },\n );\n return (await Promise.all(files)).flat();\n });\n await importStorage(bucket, \"statements\", (await Promise.all(statements)).flat(), options);\n}\n"],"names":["importData","options","existsSync","fatal","key","warning","app","bucket","db","initializeFirebase","importAdmins","importContests","importVenues","importStudents","importVariants","importWebsites","importStatements","success","deleteApp","users","load","userSchema","importUsers","contests","contestSchema","importCollection","contestConverter","schoolSchema","venueSchema","z","schools","loadContests","teachers","school","venues","contest","picomatch","pdfVariants","id","Rng","venueConverter","students","studentSchema","groupBy","venueId","studentConverter","variantsConfig","variantsConfigSchema","variants","config","uniq","fileName","path","schema","readFile","validate","variantSchema","err","variantConverter","websites","websiteSchema","websiteConverter","timestamp","statements","files","localDir","remoteDir","glob","file","ext","importStorage"],"mappings":"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"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { calcScore as
|
|
1
|
+
import { calcScore as u } from "@olinfo/quizms/models";
|
|
2
2
|
import { info as f, withProgress as p } from "@olinfo/quizms/utils-node";
|
|
3
3
|
import { variantConverter as m, studentConverter as v } from "./utils/converters-admin.js";
|
|
4
|
-
import { initializeFirebase as
|
|
4
|
+
import { initializeFirebase as w } from "./utils/initialize.js";
|
|
5
5
|
async function y() {
|
|
6
|
-
const { db: n } = await
|
|
6
|
+
const { db: n } = await w();
|
|
7
7
|
f("Updating student scores.");
|
|
8
|
-
const s = await n.collection("variants").withConverter(m).get(), c = Object.fromEntries(s.docs.map((t) => [t.id, t.data()])), r = n.collectionGroup("students").withConverter(v),
|
|
8
|
+
const s = await n.collection("variants").withConverter(m).get(), c = Object.fromEntries(s.docs.map((t) => [t.id, t.data()])), r = n.collectionGroup("students").withConverter(v), d = await r.count().get();
|
|
9
9
|
await p(
|
|
10
10
|
r.stream(),
|
|
11
|
-
|
|
11
|
+
d.data().count,
|
|
12
12
|
async (t) => {
|
|
13
13
|
const e = t.ref;
|
|
14
14
|
await n.runTransaction(async (o) => {
|
|
15
15
|
const a = (await o.get(e)).data();
|
|
16
|
-
if (!a?.
|
|
17
|
-
const i =
|
|
16
|
+
if (!a?.variantId) return;
|
|
17
|
+
const i = u(a, c[a.variantId]?.schema);
|
|
18
18
|
i !== a.score && o.update(e, { score: i });
|
|
19
19
|
});
|
|
20
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-scores.js","sources":["../../../src/cli/update-scores.ts"],"sourcesContent":["import { calcScore, type Student } from \"@olinfo/quizms/models\";\nimport { info, withProgress } from \"@olinfo/quizms/utils-node\";\nimport type { QueryDocumentSnapshot } from \"firebase-admin/firestore\";\n\nimport { studentConverter, variantConverter } from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\n\nexport default async function updateScores() {\n const { db } = await initializeFirebase();\n\n info(\"Updating student scores.\");\n\n const variantRef = db.collection(\"variants\").withConverter(variantConverter);\n const variantSnap = await variantRef.get();\n const variants = Object.fromEntries(variantSnap.docs.map((doc) => [doc.id, doc.data()]));\n\n const ref = db.collectionGroup(\"students\").withConverter(studentConverter);\n\n const count = await ref.count().get();\n await withProgress(\n ref.stream() as AsyncIterable<QueryDocumentSnapshot<Student>>,\n count.data().count,\n async (snapshot) => {\n const ref = snapshot.ref;\n await db.runTransaction(async (t) => {\n const studentSnap = await t.get(ref);\n const student = studentSnap.data();\n if (!student?.
|
|
1
|
+
{"version":3,"file":"update-scores.js","sources":["../../../src/cli/update-scores.ts"],"sourcesContent":["import { calcScore, type Student } from \"@olinfo/quizms/models\";\nimport { info, withProgress } from \"@olinfo/quizms/utils-node\";\nimport type { QueryDocumentSnapshot } from \"firebase-admin/firestore\";\n\nimport { studentConverter, variantConverter } from \"./utils/converters-admin\";\nimport { initializeFirebase } from \"./utils/initialize\";\n\nexport default async function updateScores() {\n const { db } = await initializeFirebase();\n\n info(\"Updating student scores.\");\n\n const variantRef = db.collection(\"variants\").withConverter(variantConverter);\n const variantSnap = await variantRef.get();\n const variants = Object.fromEntries(variantSnap.docs.map((doc) => [doc.id, doc.data()]));\n\n const ref = db.collectionGroup(\"students\").withConverter(studentConverter);\n\n const count = await ref.count().get();\n await withProgress(\n ref.stream() as AsyncIterable<QueryDocumentSnapshot<Student>>,\n count.data().count,\n async (snapshot) => {\n const ref = snapshot.ref;\n await db.runTransaction(async (t) => {\n const studentSnap = await t.get(ref);\n const student = studentSnap.data();\n if (!student?.variantId) return;\n const score = calcScore(student, variants[student.variantId]?.schema);\n if (score !== student.score) {\n t.update(ref, { score });\n }\n });\n },\n );\n}\n"],"names":["updateScores","db","initializeFirebase","info","variantSnap","variantConverter","variants","doc","ref","studentConverter","count","withProgress","snapshot","t","student","score","calcScore"],"mappings":";;;;AAOA,eAA8BA,IAAe;AAC3C,QAAM,EAAE,IAAAC,MAAO,MAAMC,EAAA;AAErB,EAAAC,EAAK,0BAA0B;AAG/B,QAAMC,IAAc,MADDH,EAAG,WAAW,UAAU,EAAE,cAAcI,CAAgB,EACtC,IAAA,GAC/BC,IAAW,OAAO,YAAYF,EAAY,KAAK,IAAI,CAACG,MAAQ,CAACA,EAAI,IAAIA,EAAI,KAAA,CAAM,CAAC,CAAC,GAEjFC,IAAMP,EAAG,gBAAgB,UAAU,EAAE,cAAcQ,CAAgB,GAEnEC,IAAQ,MAAMF,EAAI,MAAA,EAAQ,IAAA;AAChC,QAAMG;AAAA,IACJH,EAAI,OAAA;AAAA,IACJE,EAAM,OAAO;AAAA,IACb,OAAOE,MAAa;AAClB,YAAMJ,IAAMI,EAAS;AACrB,YAAMX,EAAG,eAAe,OAAOY,MAAM;AAEnC,cAAMC,KADc,MAAMD,EAAE,IAAIL,CAAG,GACP,KAAA;AAC5B,YAAI,CAACM,GAAS,UAAW;AACzB,cAAMC,IAAQC,EAAUF,GAASR,EAASQ,EAAQ,SAAS,GAAG,MAAM;AACpE,QAAIC,MAAUD,EAAQ,SACpBD,EAAE,OAAOL,GAAK,EAAE,OAAAO,EAAA,CAAO;AAAA,MAE3B,CAAC;AAAA,IACH;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,42 +1,43 @@
|
|
|
1
|
-
import { contestSchema as
|
|
1
|
+
import { contestSchema as m, venueSchema as s, studentSchema as c, variantSchema as a } from "@olinfo/quizms/models";
|
|
2
2
|
import { validate as f } from "@olinfo/quizms/utils";
|
|
3
3
|
import { Timestamp as o } from "firebase-admin/firestore";
|
|
4
|
-
import { cloneDeepWith as n, omit as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import { cloneDeepWith as n, omit as u, isDate as p, isString as v } from "lodash-es";
|
|
5
|
+
import { submissionSchema as S } from "../../models/submission.js";
|
|
6
|
+
import { userSchema as d } from "../../models/user.js";
|
|
7
|
+
import { websiteSchema as h } from "../../models/website.js";
|
|
8
|
+
function C(r) {
|
|
9
|
+
return n(u(r, "id"), (t) => {
|
|
10
|
+
if (p(t))
|
|
10
11
|
return o.fromDate(t);
|
|
11
|
-
if (
|
|
12
|
+
if (v(t))
|
|
12
13
|
return t.trim();
|
|
13
|
-
if (t === void 0)
|
|
14
|
-
return null;
|
|
15
14
|
});
|
|
16
15
|
}
|
|
17
|
-
function
|
|
16
|
+
function F(r) {
|
|
18
17
|
return n(r, (t) => {
|
|
19
18
|
if (t instanceof o)
|
|
20
19
|
return t.toDate();
|
|
21
20
|
});
|
|
22
21
|
}
|
|
23
|
-
function
|
|
24
|
-
const i =
|
|
22
|
+
function b(r, t) {
|
|
23
|
+
const i = F({ ...t.data(), id: t.id });
|
|
25
24
|
return f(r, i);
|
|
26
25
|
}
|
|
27
26
|
function e(r) {
|
|
28
27
|
return {
|
|
29
|
-
toFirestore: (t) =>
|
|
30
|
-
fromFirestore: (t) =>
|
|
28
|
+
toFirestore: (t) => C(t),
|
|
29
|
+
fromFirestore: (t) => b(r, t)
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
|
-
const
|
|
32
|
+
const k = e(m), q = e(s), y = e(c), z = e(S), A = e(d), B = e(a), E = e(h);
|
|
34
33
|
export {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
k as contestConverter,
|
|
35
|
+
b as parseSnapshot,
|
|
36
|
+
y as studentConverter,
|
|
37
|
+
z as submissionConverter,
|
|
38
|
+
A as userConverter,
|
|
39
|
+
B as variantConverter,
|
|
40
|
+
q as venueConverter,
|
|
41
|
+
E as websiteConverter
|
|
41
42
|
};
|
|
42
43
|
//# sourceMappingURL=converters-admin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"converters-admin.js","sources":["../../../../src/cli/utils/converters-admin.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"converters-admin.js","sources":["../../../../src/cli/utils/converters-admin.ts"],"sourcesContent":["import { contestSchema, studentSchema, variantSchema, venueSchema } from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport {\n type DocumentSnapshot,\n type FirestoreDataConverter,\n Timestamp,\n} from \"firebase-admin/firestore\";\nimport { cloneDeepWith, isDate, isString, omit } from \"lodash-es\";\nimport type z from \"zod\";\n\nimport { submissionSchema } from \"~/models/submission\";\nimport { userSchema } from \"~/models/user\";\nimport { websiteSchema } from \"~/models/website\";\n\nfunction convertToFirestore(data: object) {\n return cloneDeepWith(omit(data, \"id\"), (value) => {\n if (isDate(value)) {\n return Timestamp.fromDate(value);\n }\n if (isString(value)) {\n return value.trim();\n }\n });\n}\n\nfunction convertFromFirestore(data: object) {\n return cloneDeepWith(data, (value) => {\n if (value instanceof Timestamp) {\n return value.toDate();\n }\n });\n}\n\nexport function parseSnapshot<T>(schema: z.core.$ZodType<T>, snapshot: DocumentSnapshot): T {\n const data = convertFromFirestore({ ...snapshot.data(), id: snapshot.id });\n return validate(schema, data);\n}\n\nfunction converter<T extends object>(schema: z.core.$ZodType<T>): FirestoreDataConverter<T> {\n return {\n toFirestore: (data) => convertToFirestore(data),\n fromFirestore: (snapshot) => parseSnapshot(schema, snapshot),\n };\n}\n\nexport const contestConverter = converter(contestSchema);\nexport const venueConverter = converter(venueSchema);\nexport const studentConverter = converter(studentSchema);\nexport const submissionConverter = converter(submissionSchema);\nexport const userConverter = converter(userSchema);\nexport const variantConverter = converter(variantSchema);\nexport const websiteConverter = converter(websiteSchema);\n"],"names":["convertToFirestore","data","cloneDeepWith","omit","value","isDate","Timestamp","isString","convertFromFirestore","parseSnapshot","schema","snapshot","validate","converter","contestConverter","contestSchema","venueConverter","venueSchema","studentConverter","studentSchema","submissionConverter","submissionSchema","userConverter","userSchema","variantConverter","variantSchema","websiteConverter","websiteSchema"],"mappings":";;;;;;;AAcA,SAASA,EAAmBC,GAAc;AACxC,SAAOC,EAAcC,EAAKF,GAAM,IAAI,GAAG,CAACG,MAAU;AAChD,QAAIC,EAAOD,CAAK;AACd,aAAOE,EAAU,SAASF,CAAK;AAEjC,QAAIG,EAASH,CAAK;AAChB,aAAOA,EAAM,KAAA;AAAA,EAEjB,CAAC;AACH;AAEA,SAASI,EAAqBP,GAAc;AAC1C,SAAOC,EAAcD,GAAM,CAACG,MAAU;AACpC,QAAIA,aAAiBE;AACnB,aAAOF,EAAM,OAAA;AAAA,EAEjB,CAAC;AACH;AAEO,SAASK,EAAiBC,GAA4BC,GAA+B;AAC1F,QAAMV,IAAOO,EAAqB,EAAE,GAAGG,EAAS,QAAQ,IAAIA,EAAS,IAAI;AACzE,SAAOC,EAASF,GAAQT,CAAI;AAC9B;AAEA,SAASY,EAA4BH,GAAuD;AAC1F,SAAO;AAAA,IACL,aAAa,CAACT,MAASD,EAAmBC,CAAI;AAAA,IAC9C,eAAe,CAACU,MAAaF,EAAcC,GAAQC,CAAQ;AAAA,EAAA;AAE/D;AAEO,MAAMG,IAAmBD,EAAUE,CAAa,GAC1CC,IAAiBH,EAAUI,CAAW,GACtCC,IAAmBL,EAAUM,CAAa,GAC1CC,IAAsBP,EAAUQ,CAAgB,GAChDC,IAAgBT,EAAUU,CAAU,GACpCC,IAAmBX,EAAUY,CAAa,GAC1CC,IAAmBb,EAAUc,CAAa;"}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import * as a from "@trpc/server/adapters/express";
|
|
2
|
-
import
|
|
3
|
-
import { logger as
|
|
4
|
-
import { adminLogin as
|
|
5
|
-
import { studentLogin as
|
|
6
|
-
import {
|
|
7
|
-
import { router as
|
|
8
|
-
const h =
|
|
9
|
-
adminLogin:
|
|
10
|
-
studentLogin:
|
|
11
|
-
teacherLogin:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}), l =
|
|
2
|
+
import i from "express";
|
|
3
|
+
import { logger as n } from "firebase-functions/logger";
|
|
4
|
+
import { adminLogin as s } from "./admin.js";
|
|
5
|
+
import { studentLogin as m } from "./student.js";
|
|
6
|
+
import { teacherFinalizeVenue as c, teacherStopContestWindow as f, teacherStartContestWindow as u, teacherLogin as d } from "./teacher.js";
|
|
7
|
+
import { router as g, createContext as x } from "./trpc.js";
|
|
8
|
+
const h = g({
|
|
9
|
+
adminLogin: s,
|
|
10
|
+
studentLogin: m,
|
|
11
|
+
teacherLogin: d,
|
|
12
|
+
teacherStartContestWindow: u,
|
|
13
|
+
teacherStopContestWindow: f,
|
|
14
|
+
teacherFinalizeVenue: c
|
|
15
|
+
}), l = i();
|
|
16
16
|
l.use(
|
|
17
17
|
"/",
|
|
18
18
|
a.createExpressMiddleware({
|
|
19
19
|
router: h,
|
|
20
|
-
createContext:
|
|
21
|
-
onError(
|
|
22
|
-
const { error: r, path:
|
|
23
|
-
|
|
20
|
+
createContext: x,
|
|
21
|
+
onError(e) {
|
|
22
|
+
const { error: r, path: t, input: o, ctx: p } = e;
|
|
23
|
+
n.error(r.message, { error: r, path: t, input: o, ctx: p });
|
|
24
24
|
}
|
|
25
25
|
})
|
|
26
26
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/functions/api/index.ts"],"sourcesContent":["import * as trpcExpress from \"@trpc/server/adapters/express\";\nimport express from \"express\";\nimport { logger } from \"firebase-functions/logger\";\n\nimport { adminLogin } from \"./admin\";\nimport { studentLogin } from \"./student\";\nimport {\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/functions/api/index.ts"],"sourcesContent":["import * as trpcExpress from \"@trpc/server/adapters/express\";\nimport express from \"express\";\nimport { logger } from \"firebase-functions/logger\";\n\nimport { adminLogin } from \"./admin\";\nimport { studentLogin } from \"./student\";\nimport {\n teacherFinalizeVenue,\n teacherLogin,\n teacherStartContestWindow,\n teacherStopContestWindow,\n} from \"./teacher\";\nimport { createContext, router } from \"./trpc\";\n\nconst appRouter = router({\n adminLogin,\n studentLogin,\n teacherLogin,\n teacherStartContestWindow,\n teacherStopContestWindow,\n teacherFinalizeVenue,\n});\n\nexport type AppRouter = typeof appRouter;\n\nconst app = express();\napp.use(\n \"/\",\n trpcExpress.createExpressMiddleware({\n router: appRouter,\n createContext,\n onError(opts) {\n const { error, path, input, ctx } = opts;\n logger.error(error.message, { error, path, input, ctx });\n },\n }),\n);\n\nexport default app;\n"],"names":["appRouter","router","adminLogin","studentLogin","teacherLogin","teacherStartContestWindow","teacherStopContestWindow","teacherFinalizeVenue","app","express","trpcExpress","createContext","opts","error","path","input","ctx","logger"],"mappings":";;;;;;;AAcA,MAAMA,IAAYC,EAAO;AAAA,EACvB,YAAAC;AAAA,EACA,cAAAC;AAAA,EACA,cAAAC;AAAA,EACA,2BAAAC;AAAA,EACA,0BAAAC;AAAA,EACA,sBAAAC;AACF,CAAC,GAIKC,IAAMC,EAAA;AACZD,EAAI;AAAA,EACF;AAAA,EACAE,EAAY,wBAAwB;AAAA,IAClC,QAAQV;AAAA,IACR,eAAAW;AAAA,IACA,QAAQC,GAAM;AACZ,YAAM,EAAE,OAAAC,GAAO,MAAAC,GAAM,OAAAC,GAAO,KAAAC,MAAQJ;AACpC,MAAAK,EAAO,MAAMJ,EAAM,SAAS,EAAE,OAAAA,GAAO,MAAAC,GAAM,OAAAC,GAAO,KAAAC,GAAK;AAAA,IACzD;AAAA,EAAA,CACD;AACH;"}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { Rng as
|
|
2
|
-
import { userConverter as
|
|
3
|
-
import { db as
|
|
4
|
-
const
|
|
5
|
-
async function
|
|
6
|
-
return (await
|
|
1
|
+
import { Rng as c } from "@olinfo/quizms/utils";
|
|
2
|
+
import { userConverter as i, venueConverter as o, contestConverter as u, studentConverter as s, variantConverter as h } from "../../cli/utils/converters-admin.js";
|
|
3
|
+
import { db as n } from "../common.js";
|
|
4
|
+
const d = new c();
|
|
5
|
+
async function g(t) {
|
|
6
|
+
return (await n.doc(`contests/${t}`).withConverter(u).get()).data();
|
|
7
7
|
}
|
|
8
8
|
async function C(t) {
|
|
9
|
-
return (await
|
|
9
|
+
return (await n.doc(`venues/${t}`).withConverter(o).get()).data();
|
|
10
10
|
}
|
|
11
|
-
async function l(t,
|
|
12
|
-
return (await
|
|
11
|
+
async function l(t, e) {
|
|
12
|
+
return (await n.collection(`venues/${t}/students`).where("token", "==", e).withConverter(s).get()).docs.map((a) => a.data());
|
|
13
13
|
}
|
|
14
14
|
async function f(t) {
|
|
15
|
-
return (await
|
|
15
|
+
return (await n.collection("venues").withConverter(o).where("token", "==", t).get()).docs[0]?.data();
|
|
16
16
|
}
|
|
17
17
|
async function m(t) {
|
|
18
|
-
return (await
|
|
18
|
+
return (await n.collection("users").where("username", "==", t).withConverter(i).get()).docs[0]?.data();
|
|
19
19
|
}
|
|
20
20
|
async function y(t) {
|
|
21
|
-
const
|
|
22
|
-
return
|
|
21
|
+
const e = await n.collection("variants").withConverter(h).where("contestId", "==", t).where("isOnline", "==", !0).get();
|
|
22
|
+
return d.choice(e.docs).data();
|
|
23
23
|
}
|
|
24
|
-
async function
|
|
25
|
-
return (await
|
|
24
|
+
async function V(t, e) {
|
|
25
|
+
return (await n.collection(`venues/${t}/students`).withConverter(s).where("userDataHash", "==", e).where("disabled", "!=", !0).get()).docs[0]?.data();
|
|
26
26
|
}
|
|
27
27
|
export {
|
|
28
|
-
|
|
29
|
-
C as getParticipation,
|
|
30
|
-
f as getParticipationByToken,
|
|
31
|
-
l as getParticipationStudents,
|
|
28
|
+
g as getContest,
|
|
32
29
|
y as getRandomVariant,
|
|
33
|
-
|
|
34
|
-
m as getUser
|
|
30
|
+
V as getStudentByHash,
|
|
31
|
+
m as getUser,
|
|
32
|
+
C as getVenue,
|
|
33
|
+
f as getVenueByToken,
|
|
34
|
+
l as getVenueStudents
|
|
35
35
|
};
|
|
36
36
|
//# sourceMappingURL=queries.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.js","sources":["../../../../src/functions/api/queries.ts"],"sourcesContent":["import { Rng } from \"@olinfo/quizms/utils\";\n\nimport {\n contestConverter,\n
|
|
1
|
+
{"version":3,"file":"queries.js","sources":["../../../../src/functions/api/queries.ts"],"sourcesContent":["import { Rng } from \"@olinfo/quizms/utils\";\n\nimport {\n contestConverter,\n studentConverter,\n userConverter,\n variantConverter,\n venueConverter,\n} from \"~/cli/utils/converters-admin\";\nimport { db } from \"~/functions/common\";\n\nconst rng = new Rng();\n\nexport async function getContest(contestId: string) {\n const snapshot = await db.doc(`contests/${contestId}`).withConverter(contestConverter).get();\n return snapshot.data()!;\n}\n\nexport async function getVenue(venueId: string) {\n const snapshot = await db.doc(`venues/${venueId}`).withConverter(venueConverter).get();\n return snapshot.data();\n}\n\nexport async function getVenueStudents(venueId: string, token: string) {\n const snapshot = await db\n .collection(`venues/${venueId}/students`)\n .where(\"token\", \"==\", token)\n .withConverter(studentConverter)\n .get();\n return snapshot.docs.map((doc) => doc.data());\n}\n\nexport async function getVenueByToken(token: string) {\n const snapshot = await db\n .collection(\"venues\")\n .withConverter(venueConverter)\n .where(\"token\", \"==\", token)\n .get();\n return snapshot.docs[0]?.data();\n}\n\nexport async function getUser(username: string) {\n const snapshot = await db\n .collection(\"users\")\n .where(\"username\", \"==\", username)\n .withConverter(userConverter)\n .get();\n return snapshot.docs[0]?.data();\n}\n\nexport async function getRandomVariant(contestId: string) {\n const snapshot = await db\n .collection(\"variants\")\n .withConverter(variantConverter)\n .where(\"contestId\", \"==\", contestId)\n .where(\"isOnline\", \"==\", true)\n .get();\n return rng.choice(snapshot.docs).data();\n}\nexport async function getStudentByHash(venueId: string, userDataHash: string) {\n const snapshot = await db\n .collection(`venues/${venueId}/students`)\n .withConverter(studentConverter)\n .where(\"userDataHash\", \"==\", userDataHash)\n .where(\"disabled\", \"!=\", true)\n .get();\n return snapshot.docs[0]?.data();\n}\n"],"names":["rng","Rng","getContest","contestId","db","contestConverter","getVenue","venueId","venueConverter","getVenueStudents","token","studentConverter","doc","getVenueByToken","getUser","username","userConverter","getRandomVariant","snapshot","variantConverter","getStudentByHash","userDataHash"],"mappings":";;;AAWA,MAAMA,IAAM,IAAIC,EAAA;AAEhB,eAAsBC,EAAWC,GAAmB;AAElD,UADiB,MAAMC,EAAG,IAAI,YAAYD,CAAS,EAAE,EAAE,cAAcE,CAAgB,EAAE,IAAA,GACvE,KAAA;AAClB;AAEA,eAAsBC,EAASC,GAAiB;AAE9C,UADiB,MAAMH,EAAG,IAAI,UAAUG,CAAO,EAAE,EAAE,cAAcC,CAAc,EAAE,IAAA,GACjE,KAAA;AAClB;AAEA,eAAsBC,EAAiBF,GAAiBG,GAAe;AAMrE,UALiB,MAAMN,EACpB,WAAW,UAAUG,CAAO,WAAW,EACvC,MAAM,SAAS,MAAMG,CAAK,EAC1B,cAAcC,CAAgB,EAC9B,IAAA,GACa,KAAK,IAAI,CAACC,MAAQA,EAAI,MAAM;AAC9C;AAEA,eAAsBC,EAAgBH,GAAe;AAMnD,UALiB,MAAMN,EACpB,WAAW,QAAQ,EACnB,cAAcI,CAAc,EAC5B,MAAM,SAAS,MAAME,CAAK,EAC1B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;AAEA,eAAsBI,EAAQC,GAAkB;AAM9C,UALiB,MAAMX,EACpB,WAAW,OAAO,EAClB,MAAM,YAAY,MAAMW,CAAQ,EAChC,cAAcC,CAAa,EAC3B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;AAEA,eAAsBC,EAAiBd,GAAmB;AACxD,QAAMe,IAAW,MAAMd,EACpB,WAAW,UAAU,EACrB,cAAce,CAAgB,EAC9B,MAAM,aAAa,MAAMhB,CAAS,EAClC,MAAM,YAAY,MAAM,EAAI,EAC5B,IAAA;AACH,SAAOH,EAAI,OAAOkB,EAAS,IAAI,EAAE,KAAA;AACnC;AACA,eAAsBE,EAAiBb,GAAiBc,GAAsB;AAO5E,UANiB,MAAMjB,EACpB,WAAW,UAAUG,CAAO,WAAW,EACvC,cAAcI,CAAgB,EAC9B,MAAM,gBAAgB,MAAMU,CAAY,EACxC,MAAM,YAAY,MAAM,EAAI,EAC5B,IAAA,GACa,KAAK,CAAC,GAAG,KAAA;AAC3B;"}
|