Compare commits

..

728 commits

Author SHA1 Message Date
f699839c26 chore(deps): update module github.com/aws/aws-sdk-go to v1.51.9
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-28 09:02:29 +00:00
81a6aacbca chore(deps): update module github.com/coreos/go-oidc/v3 to v3.10.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-28 08:37:08 +00:00
6cf093db88 chore(deps): update module github.com/go-sql-driver/mysql to v1.8.1
Some checks are pending
continuous-integration/drone/push Build is running
2024-03-26 15:02:34 +00:00
4adbf7839a chore(deps): update typescript-eslint monorepo to v7.4.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-25 20:03:01 +00:00
095157d855 chore(deps): update module github.com/aws/aws-sdk-go to v1.51.7
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-25 19:03:17 +00:00
14b993196f chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-25 00:04:43 +00:00
a113289461 chore(deps): update dependency vite to v5.2.6
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-24 14:03:17 +00:00
a33d667f3f chore(deps): update dependency vite to v5.2.5
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-24 10:03:14 +00:00
a99126f268 chore(deps): update dependency vite to v5.2.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-23 10:03:11 +00:00
930195a6e9 chore(deps): update dependency vite to v5.2.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-22 14:03:12 +00:00
d2be8c8875 chore(deps): update module github.com/aws/aws-sdk-go to v1.51.5
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-21 19:03:40 +00:00
780f8af07e chore(deps): update module github.com/aws/aws-sdk-go to v1.51.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-21 06:05:30 +00:00
af201ab897 chore(deps): update dependency vite to v5.2.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-20 14:03:13 +00:00
a9b79fd140 chore(deps): update dependency vite to v5.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-20 10:03:07 +00:00
b4450b03d2 chore(deps): update module github.com/aws/aws-sdk-go to v1.51.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-19 19:03:16 +00:00
6c13c6aae6 chore(deps): update dependency svelte-check to v3.6.8
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-19 12:02:55 +00:00
62d5d86459 chore(deps): update typescript-eslint monorepo to v7.3.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-19 00:03:49 +00:00
1e732f53d7 chore(deps): update module github.com/aws/aws-sdk-go to v1.51.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-18 19:06:04 +00:00
8d5131508d chore(deps): update typescript-eslint monorepo to v7.3.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-18 18:03:10 +00:00
10de670834 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-18 00:04:06 +00:00
cde844ed03 chore(deps): update module github.com/aws/aws-sdk-go to v1.51.1
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-15 19:03:16 +00:00
67ed637d18 chore(deps): update module github.com/aws/aws-sdk-go to v1.51.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-14 19:03:16 +00:00
c65ded7859 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.38
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-13 20:02:38 +00:00
7c345bf095 chore(deps): update dependency @sveltejs/kit to v2.5.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-13 17:02:55 +00:00
ae42d381c8 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.37
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-12 19:03:14 +00:00
cade4de87f chore(deps): update typescript-eslint monorepo to v7.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-12 16:29:23 +00:00
1e317fd4dc chore(deps): update dependency vite to v5.1.6
Some checks are pending
continuous-integration/drone/push Build is running
2024-03-12 16:28:22 +00:00
ab1ad3930b chore(deps): update module github.com/aws/aws-sdk-go to v1.50.36
Some checks are pending
continuous-integration/drone/push Build is running
2024-03-12 16:05:08 +00:00
f06974cc80 chore(deps): update dependency svelte-check to v3.6.7
Some checks are pending
continuous-integration/drone/push Build is running
2024-03-12 15:02:51 +00:00
02ced9e2c2 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-11 00:04:35 +00:00
00ced139f3 chore(deps): update module github.com/go-sql-driver/mysql to v1.8.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-09 08:02:46 +00:00
df918a9379 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.35
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 21:02:46 +00:00
9b49cc5857 chore(deps): update dependency @sveltejs/kit to v2.5.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 18:02:54 +00:00
b5e7e3cfd0 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.34
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-07 21:03:14 +00:00
f3adddd705 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.33
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-06 21:03:16 +00:00
ebc3f3257e chore(deps): update module golang.org/x/oauth2 to v0.18.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-05 21:04:26 +00:00
11f02ad0c9 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.32
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-05 20:03:22 +00:00
256233d002 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.31
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-04 21:04:35 +00:00
2066a3119a chore(deps): update dependency vite to v5.1.5
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-04 20:02:43 +00:00
54973bc27a chore(deps): update typescript-eslint monorepo to v7.1.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-04 18:02:41 +00:00
0dfeef5075 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-04 00:04:06 +00:00
b1df56ed48 chore(deps): update typescript-eslint monorepo to v7.1.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-02 20:06:20 +00:00
bbca81003e chore(deps): update module github.com/aws/aws-sdk-go to v1.50.30
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-02 19:06:00 +00:00
5698c191bb chore(deps): update dependency svelte-check to v3.6.6
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-02 18:06:15 +00:00
66214b50d8 chore(deps): update dependency prettier-plugin-svelte to v3.2.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-02 17:07:04 +00:00
6b4108b7a0 chore(deps): update dependency @sveltejs/kit to v2.5.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-02 15:41:27 +00:00
072dbc9dd7 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-26 00:05:54 +00:00
ea3620db34 chore(deps): update dependency eslint to v8.57.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-23 21:05:53 +00:00
d28f0fc335 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.25
Some checks are pending
continuous-integration/drone/push Build is running
2024-02-23 20:04:38 +00:00
ab5b535f12 chore(deps): update dependency svelte to v4.2.12
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-23 08:04:09 +00:00
2d444d9098 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.24
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-22 20:04:53 +00:00
2778cdd926 chore(deps): update dependency @sveltejs/kit to v2.5.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-22 05:04:05 +00:00
01c0795a5b chore(deps): update module github.com/aws/aws-sdk-go to v1.50.23
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-21 20:04:30 +00:00
da3abc4540 chore(deps): update dependency vite to v5.1.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-21 12:04:07 +00:00
0fb7232e1c chore(deps): update module github.com/aws/aws-sdk-go to v1.50.22
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-20 20:04:28 +00:00
fcf252ab40 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.21
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-19 20:04:31 +00:00
d67a3d9aa7 chore(deps): update typescript-eslint monorepo to v7.0.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-19 18:04:59 +00:00
8cc2bc37ad chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-19 00:05:47 +00:00
2a87a7ef9b chore(deps): update module github.com/aws/aws-sdk-go to v1.50.20
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-16 20:04:29 +00:00
bcb4990956 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.19
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-15 21:03:57 +00:00
f13a6b03b8 chore(deps): update dependency svelte to v4.2.11
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-15 18:04:16 +00:00
6ed4b287a8 chore(deps): update dependency vite to v5.1.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-15 14:04:19 +00:00
9536788a31 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.18
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-14 20:04:24 +00:00
2141879718 chore(deps): update dependency vite to v5.1.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-14 15:04:06 +00:00
b45441e843 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.17
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-14 12:05:49 +00:00
05350aeac4 chore(deps): update dependency prettier-plugin-svelte to v3.2.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-14 11:03:58 +00:00
f13f25ce64 chore(deps): update dependency prettier-plugin-svelte to v3.2.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2024-02-13 14:04:18 +00:00
e647e098fe chore(deps): update typescript-eslint monorepo to v7
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-13 11:07:41 +00:00
a5e190eecf chore(deps): update module github.com/aws/aws-sdk-go to v1.50.16
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-12 21:04:08 +00:00
5760a69d6b chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-12 00:05:12 +00:00
d215595788 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.15
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-09 20:04:50 +00:00
ab40c7c000 chore(deps): update dependency vite to v5.1.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-09 10:04:01 +00:00
6b3e38e205 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.14
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-08 20:04:20 +00:00
49f42dd477 chore(deps): update dependency svelte-check to v3.6.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-08 17:03:57 +00:00
f5c384564b chore(deps): update module golang.org/x/oauth2 to v0.17.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-08 14:04:06 +00:00
ae4d9484c3 chore(deps): update dependency vite to v5.1.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-08 11:04:01 +00:00
54f0231f68 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.13
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-07 20:04:29 +00:00
f10d29a917 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.12
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-06 20:04:11 +00:00
9939ecd7a3 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.11
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-05 20:06:09 +00:00
7882ba83f5 chore(deps): update typescript-eslint monorepo to v6.21.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-05 18:06:22 +00:00
4fefd67f6b chore(deps): update dependency svelte to v4.2.10
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-05 17:04:03 +00:00
5ae8d4e27a chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-05 00:05:12 +00:00
3b3c13771c chore(deps): update dependency prettier to v3.2.5
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-04 06:03:54 +00:00
b4723dc66a chore(deps): update module github.com/aws/aws-sdk-go to v1.50.10
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-02 20:03:53 +00:00
2812b37947 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.9
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-01 20:04:01 +00:00
919ceec689 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.7
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-30 20:04:10 +00:00
81cbb6fd11 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.6
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-29 20:04:21 +00:00
ec1d40f469 chore(deps): update typescript-eslint monorepo to v6.20.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-29 18:04:36 +00:00
637337aecf chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-29 00:05:25 +00:00
e7f5062ba6 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.5
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-26 20:03:54 +00:00
1cd9275c76 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-25 20:06:15 +00:00
eb3bd7d52a chore(deps): update dependency @sveltejs/kit to v2.5.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-25 19:03:53 +00:00
dfb010df18 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-24 20:03:44 +00:00
288d5b61dc chore(deps): update module github.com/aws/aws-sdk-go to v1.50.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-23 22:04:00 +00:00
8ed8783195 chore(deps): update dependency @sveltejs/kit to v2.4.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-22 23:04:00 +00:00
b863d3b319 chore(deps): update dependency @sveltejs/kit to v2.4.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-22 21:05:53 +00:00
a03f3dc0a2 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-22 20:03:47 +00:00
1f40077b48 chore(deps): update typescript-eslint monorepo to v6.19.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-22 18:04:21 +00:00
96768ab7ae chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-22 00:05:07 +00:00
a721ad53f6 chore(deps): update module github.com/aws/aws-sdk-go to v1.50.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-19 20:05:53 +00:00
b7bb28e80c chore(deps): update dependency @sveltejs/kit to v2.4.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-19 19:04:03 +00:00
9690795812 chore(deps): update dependency vite to v5.0.12
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-19 14:04:00 +00:00
44f641bb58 chore(deps): update dependency @sveltejs/kit to v2.4.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-19 00:04:18 +00:00
dfc011ca20 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.24
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-18 20:03:52 +00:00
d672baec43 chore(deps): update dependency @sveltejs/kit to v2.3.5
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-18 05:04:13 +00:00
07c2a3615c chore(deps): update dependency @sveltejs/kit to v2.3.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-17 23:03:58 +00:00
85871ac63c chore(deps): update module github.com/aws/aws-sdk-go to v1.49.23
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-17 20:03:47 +00:00
5179ebd1fb chore(deps): update module github.com/protonmail/go-crypto to v1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-17 14:03:28 +00:00
a15661a661 chore(deps): update dependency svelte to v4.2.9
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-17 12:05:44 +00:00
15d99de98c chore(deps): update dependency prettier to v3.2.4
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-17 11:03:59 +00:00
c6524825f2 chore(deps): update dependency prettier to v3.2.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-17 04:03:54 +00:00
3ed437c181 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.22
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-16 20:04:03 +00:00
09473aa551 chore(deps): update dependency @sveltejs/kit to v2.3.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-16 15:03:50 +00:00
188aae4dd3 chore(deps): update typescript-eslint monorepo to v6.19.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-15 18:04:23 +00:00
bfe090ac50 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-15 00:04:54 +00:00
c373032830 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.21
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-14 06:03:56 +00:00
3be7bf0e7c chore(deps): update dependency prettier to v3.2.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-14 04:04:31 +00:00
406c41856f chore(deps): update module github.com/aws/aws-sdk-go to v1.49.20
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-12 21:05:43 +00:00
40ea13e88d chore(deps): update dependency prettier to v3.2.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-12 20:04:01 +00:00
4dac5b527b chore(deps): update dependency prettier to v3.2.0
Some checks failed
continuous-integration/drone/push Build is failing
2024-01-12 17:03:50 +00:00
301f77039f chore(deps): update dependency svelte-check to v3.6.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-12 10:03:59 +00:00
866c139e37 chore(deps): update dependency @sveltejs/kit to v2.3.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-11 21:06:06 +00:00
074d7b5782 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.19
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-11 20:03:49 +00:00
6bcd211de0 chore(deps): update dependency @sveltejs/kit to v2.3.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-11 17:04:07 +00:00
a4d47771ff chore(deps): update module github.com/aws/aws-sdk-go to v1.49.18
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-10 20:03:50 +00:00
337f4d975c chore(deps): update dependency @sveltejs/kit to v2.3.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-10 17:03:48 +00:00
f2a82b9db6 chore(deps): update dependency @sveltejs/kit to v2.2.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-10 15:03:48 +00:00
b6af81a8b6 chore(deps): update dependency @sveltejs/kit to v2.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-09 21:04:27 +00:00
6016ad8dcd chore(deps): update dependency @sveltejs/kit to v2.1.2
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-09 17:04:00 +00:00
b4ff6314d6 migration to SvelteKit 2
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-09 10:04:13 +01:00
1c4afb2461 migration to Svelte 4 2024-01-09 10:02:34 +01:00
8138af8e7c Remove arm build
Some checks are pending
continuous-integration/drone/push Build is running
2024-01-09 10:00:54 +01:00
8630d66e64 chore(deps): update typescript-eslint monorepo to v6.18.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-08 23:04:51 +00:00
30dd9103f1 chore(deps): update module golang.org/x/oauth2 to v0.16.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-08 21:07:52 +00:00
8d2a08258d chore(deps): update module github.com/aws/aws-sdk-go to v1.49.17
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-08 20:04:50 +00:00
b0d0c70cfc chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-08 00:05:36 +00:00
04cd4bc283 chore(deps): update typescript-eslint monorepo to v6.18.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-06 14:04:46 +00:00
586a89ee90 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.16
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-05 20:04:13 +00:00
bf78306e98 chore(deps): replace dependency eslint-plugin-svelte3 with eslint-plugin-svelte ^2.35.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2024-01-05 10:04:04 +00:00
e1eb999e70 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.15
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-04 21:03:59 +00:00
67d6b9a805 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.14
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-03 20:03:56 +00:00
2e92a65968 chore(deps): update typescript-eslint monorepo to v6.17.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-01 18:03:58 +00:00
2dc34d43a7 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-01 10:05:45 +00:00
f047f72ad1 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.13
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-31 15:43:41 +00:00
749ec9a41e chore(deps): update module github.com/aws/aws-sdk-go to v1.49.11
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-27 20:03:40 +00:00
5326a37f8c chore(deps): update module github.com/aws/aws-sdk-go to v1.49.10
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-26 20:04:07 +00:00
c2aee54a6b chore(deps): update typescript-eslint monorepo to v6.16.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-25 18:04:03 +00:00
4f211afe50 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-25 00:05:00 +00:00
f38e44802d chore(deps): update module github.com/aws/aws-sdk-go to v1.49.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-22 20:04:06 +00:00
58e90e640d chore(deps): update module github.com/aws/aws-sdk-go to v1.49.8
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-21 20:04:06 +00:00
8e9c4faf45 chore(deps): update typescript-eslint monorepo to v6.15.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-18 21:06:52 +00:00
fc2952289d chore(deps): update module github.com/aws/aws-sdk-go to v1.49.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-18 20:04:10 +00:00
d0cd58e365 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-18 00:04:51 +00:00
44bb3d1713 chore(deps): update dependency eslint to v8.56.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-15 23:04:00 +00:00
a77cca6278 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-15 20:04:05 +00:00
4ed9507c76 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-14 20:03:58 +00:00
84cc469b99 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-13 21:05:54 +00:00
3f8dd8edd7 chore(deps): update dependency @sveltejs/kit to v1.30.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-13 20:03:44 +00:00
3ee467d0b9 chore(deps): update dependency @sveltejs/kit to v1.30.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-13 16:03:42 +00:00
7362168eff chore(deps): update dependency @sveltejs/kit to v1.30.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-13 10:03:35 +00:00
73190dcd3f chore(deps): update dependency @sveltejs/kit to v1.30.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-13 06:04:06 +00:00
523873ae9d chore(deps): update module github.com/aws/aws-sdk-go to v1.49.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-12 20:04:08 +00:00
d5c483fc0c chore(deps): update dependency @sveltejs/kit to v1.29.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-12 15:03:32 +00:00
0d0f76fc38 chore(deps): update dependency @sveltejs/kit to v1.29.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-11 22:03:40 +00:00
33b6400754 chore(deps): update module github.com/aws/aws-sdk-go to v1.49.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-11 20:03:48 +00:00
37729609b4 chore(deps): update typescript-eslint monorepo to v6.14.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-11 18:03:35 +00:00
83a171e9b9 chore(deps): update dependency @sveltejs/kit to v1.28.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-11 04:03:26 +00:00
bb0525521e chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-11 00:04:27 +00:00
4e056d08f2 chore(deps): update dependency prettier to v3.1.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-10 09:06:43 +00:00
2f3e9b82c7 chore(deps): update module github.com/coreos/go-oidc/v3 to v3.9.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-10 08:57:45 +00:00
84ff753582 chore(deps): update dependency typescript to v5.3.3
Some checks are pending
continuous-integration/drone/push Build is running
2023-12-09 22:05:27 +00:00
a7d31c6b92 chore(deps): update alpine docker tag to v3.19
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-09 20:29:05 +00:00
d20941931a chore(deps): update module github.com/aws/aws-sdk-go to v1.48.16
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-08 20:03:54 +00:00
e6c1e92319 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.14
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-06 20:05:12 +00:00
a1f3b374eb chore(deps): update module github.com/aws/aws-sdk-go to v1.48.13
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-05 20:03:56 +00:00
6aa9d131d6 chore(deps): update dependency @sveltejs/kit to v1.27.7
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-05 16:03:44 +00:00
2be7460a7b chore(deps): update module github.com/aws/aws-sdk-go to v1.48.12
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-04 20:05:51 +00:00
1c97e8ff74 chore(deps): update typescript-eslint monorepo to v6.13.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-04 18:03:59 +00:00
ec2438a6fe chore(deps): update dependency vite to v4.5.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-04 10:03:46 +00:00
beaf39dba1 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-04 00:05:03 +00:00
26c3f16f7a chore(deps): update dependency eslint-config-prettier to v9.1.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-02 10:03:38 +00:00
d598a9c6ef chore(deps): update dependency eslint to v8.55.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-01 22:03:51 +00:00
e5b2812626 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.11
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-01 20:03:43 +00:00
97e874dc7d chore(deps): update module github.com/aws/aws-sdk-go to v1.48.10
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-30 20:03:26 +00:00
d5d86d0f56 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-30 02:03:59 +00:00
f553144c6e chore(deps): update module github.com/aws/aws-sdk-go to v1.48.8
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-29 20:03:49 +00:00
7586cf0f14 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.7
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-28 20:03:27 +00:00
0310d4d82a chore(deps): update typescript-eslint monorepo to v6.13.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-28 10:03:52 +00:00
329459af7e chore(deps): update module github.com/coreos/go-oidc/v3 to v3.8.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-28 08:03:57 +00:00
769d56a42e chore(deps): update module github.com/aws/aws-sdk-go to v1.48.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-28 06:03:47 +00:00
eac92a200c chore(deps): update module golang.org/x/oauth2 to v0.15.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-28 00:05:14 +00:00
f096cd686a chore(deps): update module github.com/aws/aws-sdk-go to v1.48.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-27 21:03:26 +00:00
611846fd17 chore(deps): update typescript-eslint monorepo to v6.13.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-27 18:03:59 +00:00
9969c0fd95 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-27 05:04:04 +00:00
ddb4f44892 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-27 00:05:03 +00:00
26464ebd20 chore(deps): update dependency svelte-check to v3.6.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-24 17:03:37 +00:00
b0ae313de9 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-22 21:04:00 +00:00
403a0d4288 chore(deps): update dependency prettier-plugin-svelte to v3.1.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-22 19:03:42 +00:00
6a06290359 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-22 13:06:12 +00:00
d34bcf1e8f chore(deps): update dependency svelte-check to v3.6.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-22 12:03:46 +00:00
31a4547e3b chore(deps): update dependency prettier-plugin-svelte to v3.1.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-20 23:03:35 +00:00
bf6bee03fb chore(deps): update module github.com/aws/aws-sdk-go to v1.48.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-20 21:03:32 +00:00
0b7b545352 chore(deps): update typescript-eslint monorepo to v6.12.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-20 18:04:28 +00:00
2f2fe68b96 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-20 00:04:39 +00:00
91dbf01408 chore(deps): update dependency eslint to v8.54.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-17 22:05:58 +00:00
a3e665c435 chore(deps): update module github.com/aws/aws-sdk-go to v1.48.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-17 21:03:40 +00:00
912997835a chore(deps): update module github.com/aws/aws-sdk-go to v1.47.13
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-16 20:03:47 +00:00
3ace06da4f chore(deps): update module github.com/aws/aws-sdk-go to v1.47.12
Some checks failed
continuous-integration/drone/push Build is failing
2023-11-15 20:04:45 +00:00
6f1b9e68cb chore(deps): update module github.com/aws/aws-sdk-go to v1.47.11
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-14 20:03:25 +00:00
a3c1317bae chore(deps): update dependency @sveltejs/kit to v1.27.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-14 13:03:41 +00:00
072507ab92 chore(deps): update module nhooyr.io/websocket to v1.8.10
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-14 12:25:49 +00:00
4bbf8f6d03 chore(deps): update node.js to v21
Some checks are pending
continuous-integration/drone/push Build is pending
2023-11-14 09:03:35 +00:00
d2d03d863e chore(deps): update typescript-eslint monorepo to v6.11.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-13 21:05:03 +00:00
bf8e196b36 chore(deps): update module github.com/aws/aws-sdk-go to v1.47.10
Some checks are pending
continuous-integration/drone/push Build is running
2023-11-13 20:03:49 +00:00
3866dd9880 chore(deps): update dependency prettier to v3.1.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-13 03:08:16 +00:00
917895adc9 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-13 01:06:11 +00:00
780f05e05d chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-13 00:04:31 +00:00
01375096f8 chore(deps): update module github.com/aws/aws-sdk-go to v1.47.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-10 21:03:21 +00:00
0a36c7ad3c chore(deps): update dependency svelte-check to v3.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-10 17:03:27 +00:00
a0cac2c577 chore(deps): update dependency prettier-plugin-svelte to v3.1.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-10 09:03:40 +00:00
5b3ab94c3e chore(deps): update module github.com/aws/aws-sdk-go to v1.47.8
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-09 20:03:20 +00:00
a418c89672 chore(deps): update module github.com/aws/aws-sdk-go to v1.47.7
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-09 14:05:02 +00:00
e92130b998 chore(deps): update dependency @sveltejs/kit to v1.27.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-09 13:03:34 +00:00
8a3f7ba587 chore(deps): update module golang.org/x/oauth2 to v0.14.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-08 23:03:20 +00:00
dfc541011c chore(deps): update module github.com/aws/aws-sdk-go to v1.47.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-08 20:03:29 +00:00
0e8d0d5d71 chore(deps): update module github.com/aws/aws-sdk-go to v1.47.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-07 21:03:38 +00:00
c44be1a9c2 chore(deps): update module github.com/aws/aws-sdk-go to v1.47.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-06 22:03:11 +00:00
229bf88646 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-06 21:10:05 +00:00
5f43d21159 chore(deps): update dependency eslint to v8.53.0
Some checks are pending
continuous-integration/drone/push Build is running
2023-11-06 20:03:44 +00:00
c758413542 chore(deps): update module github.com/aws/aws-sdk-go to v1.47.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-06 19:57:24 +00:00
f8dacb1ffc chore(deps): update dependency @sveltejs/kit to v1.27.3
Some checks are pending
continuous-integration/drone/push Build is running
2023-11-06 19:05:57 +00:00
8694988327 chore(deps): update typescript-eslint monorepo to v6.10.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-06 18:04:08 +00:00
53ed4d7904 chore(deps): update module github.com/aws/aws-sdk-go to v1.47.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-02 19:03:40 +00:00
fa8c5d43dd chore(deps): update module github.com/aws/aws-sdk-go to v1.47.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-01 19:03:30 +00:00
6fea42006a chore(deps): update module github.com/aws/aws-sdk-go to v1.47.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-31 19:04:39 +00:00
3f76f680be chore(deps): update module github.com/aws/aws-sdk-go to v1.46.7
Some checks failed
continuous-integration/drone/push Build is failing
2023-10-30 19:05:17 +00:00
d26c33522c chore(deps): update typescript-eslint monorepo to v6.9.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-30 18:03:38 +00:00
aa9a75e3cb chore(deps): update dependency @sveltejs/kit to v1.27.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-30 13:03:13 +00:00
6a99c23218 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-30 00:04:13 +00:00
cccaacc606 chore(deps): update module github.com/aws/aws-sdk-go to v1.46.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-27 19:03:14 +00:00
b024e0abba chore(deps): update module github.com/aws/aws-sdk-go to v1.46.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-27 12:04:57 +00:00
7d19091aa1 chore(deps): update dependency @sveltejs/kit to v1.27.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-27 10:32:26 +00:00
e3854a46ab chore(deps): update module nhooyr.io/websocket to v1.8.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-25 07:32:21 +00:00
a09028978d chore(deps): update module github.com/coreos/go-oidc/v3 to v3.7.0
Some checks are pending
continuous-integration/drone/push Build is running
2023-10-24 20:05:36 +00:00
4a8ce4c464 chore(deps): update module github.com/aws/aws-sdk-go to v1.46.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-24 19:04:11 +00:00
5538804baf chore(deps): update typescript-eslint monorepo to v6.9.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-23 21:08:55 +00:00
32b9393a77 chore(deps): update module github.com/aws/aws-sdk-go to v1.46.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-23 19:48:34 +00:00
bddd8bacf6 chore(deps): update dependency @sveltejs/kit to v1.27.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-23 16:06:09 +00:00
5b7f120280 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-23 02:05:29 +00:00
26dcff2728 chore(deps): update dependency eslint to v8.52.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-20 22:14:34 +00:00
d0b7922169 chore(deps): update module github.com/aws/aws-sdk-go to v1.46.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-20 21:06:19 +00:00
7528e5fa61 chore(deps): update module github.com/aws/aws-sdk-go to v1.46.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-19 21:06:13 +00:00
79464b201c chore(deps): update module github.com/aws/aws-sdk-go to v1.45.28
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-18 19:06:04 +00:00
4be1496f40 chore(deps): update dependency @sveltejs/kit to v1.26.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-18 16:06:07 +00:00
d6d0d747b7 chore(deps): update dependency vite to v4.5.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-18 10:06:16 +00:00
a19c246cb5 chore(deps): lock file maintenance
Some checks failed
continuous-integration/drone/push Build is failing
2023-10-17 20:08:34 +00:00
9a25d1473f chore(deps): update module github.com/aws/aws-sdk-go to v1.45.27
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-10-17 19:06:27 +00:00
ffec258b12 chore(deps): update dependency @sveltejs/kit to v1.25.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-09 21:05:46 +00:00
80da7fefe6 chore(deps): update typescript-eslint monorepo to v6.7.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-09 18:06:01 +00:00
9376eba6ae chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-09 00:06:35 +00:00
a27d9bb0ce chore(deps): update module github.com/aws/aws-sdk-go to v1.45.24
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-06 19:05:54 +00:00
ead4458938 chore(deps): update module golang.org/x/oauth2 to v0.13.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-06 09:05:43 +00:00
db4c69e45c chore(deps): update module github.com/aws/aws-sdk-go to v1.45.23
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-05 20:29:23 +00:00
a8c9497cd3 chore(deps): update dependency vite to v4.4.11
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-05 08:05:44 +00:00
a2d5dae4de chore(deps): update module github.com/aws/aws-sdk-go to v1.45.22
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-04 19:05:58 +00:00
6dedf2d4aa chore(deps): update module github.com/aws/aws-sdk-go to v1.45.21
Some checks failed
continuous-integration/drone/push Build is failing
2023-10-03 20:05:33 +00:00
a1aa4b0b2c chore(deps): update dependency vite to v4.4.10
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-03 17:05:54 +00:00
fea39b4c34 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.20
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-02 19:08:40 +00:00
1c4b3ed253 chore(deps): update typescript-eslint monorepo to v6.7.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-02 18:06:02 +00:00
e8ffe5f6aa chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-02 00:06:25 +00:00
97f27ce914 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.19
Some checks failed
continuous-integration/drone/push Build is failing
2023-09-28 19:06:10 +00:00
84d9409411 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.18
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-27 20:01:40 +00:00
fa07dd2e29 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.17
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-26 19:06:51 +00:00
1715bc7cb7 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.16
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-25 19:09:13 +00:00
808f3a2783 chore(deps): update typescript-eslint monorepo to v6.7.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-25 18:06:01 +00:00
85c39b4547 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-25 00:05:58 +00:00
abfcd2fd27 chore(deps): update dependency @sveltejs/kit to v1.25.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-24 22:05:55 +00:00
f75a13b75e chore(deps): update github.com/protonmail/go-crypto digest to afb1ddc
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-23 07:05:39 +00:00
9f8b7da0cc chore(deps): update dependency eslint to v8.50.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-22 22:53:06 +00:00
57cd7c8198 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.15
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-22 19:07:34 +00:00
5e54ad3450 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.14
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-22 08:05:40 +00:00
96c7c85ca3 chore(deps): update dependency dayjs to v1.11.10
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 20:08:28 +00:00
1850fdca83 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.13
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 19:06:16 +00:00
5a8cc3677a chore(deps): update dependency svelte-check to v3.5.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 15:06:07 +00:00
fdb3fb9992 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.12
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-18 19:09:08 +00:00
9522fed68f chore(deps): update typescript-eslint monorepo to v6.7.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-18 18:06:24 +00:00
a94ca6ad1d chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-18 00:06:19 +00:00
50b61b17ec chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-15 22:06:13 +00:00
7f98702102 chore(deps): update typescript-eslint monorepo to v6.7.0
Some checks are pending
continuous-integration/drone/push Build is running
2023-09-15 21:45:44 +00:00
7fdd25624e chore(deps): update dependency @sveltejs/kit to v1.25.0
Some checks are pending
continuous-integration/drone/push Build is running
2023-09-15 21:12:43 +00:00
6948b91bd5 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.11
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-15 20:06:48 +00:00
e5dcb10429 chore(deps): update dependency eslint to v8.49.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-08 22:05:29 +00:00
440fffb84b chore(deps): update module github.com/aws/aws-sdk-go to v1.45.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-08 19:05:53 +00:00
23501dc139 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-07 19:05:39 +00:00
c5d20c8fed chore(deps): update module github.com/aws/aws-sdk-go to v1.45.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-06 19:06:18 +00:00
f1f013a784 chore(deps): update typescript-eslint monorepo to v6.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-06 04:07:41 +00:00
02f9131d6b chore(deps): update module golang.org/x/oauth2 to v0.12.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-06 03:09:41 +00:00
fff8f3dad4 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-05 22:06:34 +00:00
df4da19589 chore(deps): update dependency @sveltejs/kit to v1.24.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-04 12:05:40 +00:00
71576af4d7 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-04 00:06:44 +00:00
273eeaf753 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-01 19:05:45 +00:00
3facc41327 chore(deps): update module github.com/aws/aws-sdk-go to v1.45.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-31 20:06:11 +00:00
aeccd4b50d chore(deps): update module github.com/aws/aws-sdk-go to v1.45.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-30 19:05:51 +00:00
540b67841c chore(deps): update dependency @sveltejs/kit to v1.24.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-30 00:15:14 +00:00
ec804681a5 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.334
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-29 23:05:57 +00:00
6634f30b50 chore(deps): update dependency prettier to v3.0.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-29 13:08:04 +00:00
622ed2df63 chore(deps): update dependency svelte-check to v3.5.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-29 12:05:27 +00:00
6e86f181d4 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.333
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-28 19:05:42 +00:00
bb9f6ddb5d chore(deps): update github.com/protonmail/go-crypto digest to 3c4c8a2
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-28 09:05:24 +00:00
cf46d8a7cb chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-28 00:06:13 +00:00
c5ccd71de5 Colorize results
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-26 23:53:50 +02:00
71910b9da8 work: Display checks steps of Check
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-26 18:25:16 +02:00
c63a210ea7 Add warning state
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-26 10:04:51 +02:00
0cca11e1da chore(deps): update dependency eslint to v8.48.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-25 20:07:41 +00:00
36441fcf85 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.332
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-25 19:07:51 +00:00
ff8ad740c3 chore(deps): update dependency typescript to v5.2.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-24 20:07:45 +00:00
b8859595fc chore(deps): update module github.com/aws/aws-sdk-go to v1.44.331
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-24 19:06:37 +00:00
86b4e45320 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.330
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-23 19:08:04 +00:00
6d2a2d6c3a chore(deps): update dependency @sveltejs/kit to v1.23.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-23 18:05:43 +00:00
cc7b772b2a chore(deps): update typescript-eslint monorepo to v6.4.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 20:07:47 +00:00
b5ec9ba012 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.329
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-22 19:05:41 +00:00
e975a006ca chore(deps): update module github.com/aws/aws-sdk-go to v1.44.328
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-21 19:06:00 +00:00
b078d648bc chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-21 00:06:32 +00:00
180b6eaf0a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.327
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-18 19:06:17 +00:00
8bebc2770f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.326
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-17 19:05:47 +00:00
eabd7a89a4 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.325
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-16 19:05:44 +00:00
c5bf52801a chore(deps): update typescript-eslint monorepo to v6.4.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-15 20:07:57 +00:00
af02b73541 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.324
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-15 19:05:39 +00:00
5b0a8a405b chore(deps): update dependency prettier to v3.0.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-15 16:05:46 +00:00
a3aa1a6e1b chore(deps): update module github.com/aws/aws-sdk-go to v1.44.323
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-14 19:05:48 +00:00
1be761d4b0 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.322
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-14 06:08:48 +00:00
673ccba290 chore(deps): update dependency @sveltejs/kit to v1.22.6
Some checks are pending
continuous-integration/drone/push Build is running
2023-08-14 05:05:32 +00:00
0d63b3d8d1 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-14 00:06:53 +00:00
5704bab1fd chore(deps): update dependency eslint to v8.47.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-11 16:09:03 +00:00
b43c009638 chore(deps): update dependency svelte-check to v3.5.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-11 15:06:12 +00:00
2a5af7e978 chore(deps): update dependency @sveltejs/kit to v1.22.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-10 22:05:31 +00:00
61c0db2a26 chore(deps): update dependency eslint-config-prettier to v9
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-10 07:53:17 +00:00
c16b3cb16f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.318
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-07 19:05:26 +00:00
81489d7a2d chore(deps): update dependency vite to v4.4.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-07 10:05:17 +00:00
5a8a4f6796 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-07 00:06:05 +00:00
9e2440cb3a chore(deps): update module golang.org/x/oauth2 to v0.11.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-05 20:37:40 +00:00
1047959c72 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.317
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-04 19:05:30 +00:00
1a7eb222ed chore(deps): update dependency eslint-config-prettier to v8.10.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-03 20:07:31 +00:00
f2a99495b3 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.316
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-03 19:05:39 +00:00
9109e14693 chore(deps): update dependency prettier to v3.0.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-03 07:05:35 +00:00
faf747a90b chore(deps): update module github.com/aws/aws-sdk-go to v1.44.315
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 19:05:28 +00:00
acc4a78338 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.314
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 08:31:37 +00:00
95c3d9baa1 chore(deps): update dependency vite to v4.4.8
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-02 07:11:30 +00:00
5814dd4d9e chore(deps): update typescript-eslint monorepo to v6.2.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-31 20:07:38 +00:00
69ad134c5c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.313
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-31 19:06:09 +00:00
89e2c8b363 chore(deps): update dependency @sveltejs/kit to v1.22.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-31 12:05:27 +00:00
3503e0fcc1 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-31 00:06:14 +00:00
d3f41fc8a3 chore(deps): update dependency eslint to v8.46.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-30 19:07:33 +00:00
69863b34ba chore(deps): update module github.com/aws/aws-sdk-go to v1.44.312
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-30 18:07:34 +00:00
c633c2bdad chore(deps): update dependency prettier-plugin-svelte to v3.0.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-30 17:07:34 +00:00
b088ca44c0 chore(deps): update dependency @sveltejs/adapter-static to v2.0.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-30 16:06:22 +00:00
d1a25a9062 chore(deps): update dependency prettier-plugin-svelte to v3.0.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-27 10:05:47 +00:00
ad7b7807f9 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.309
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-26 21:05:27 +00:00
504956847e chore(deps): update typescript-eslint monorepo to v6.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-26 20:15:06 +00:00
d04988e8a4 chore(deps): update dependency prettier-plugin-svelte to v3.0.1
Some checks are pending
continuous-integration/drone/push Build is running
2023-07-26 20:14:30 +00:00
b57d95ff31 chore(deps): update dependency tslib to v2.6.1
Some checks are pending
continuous-integration/drone/push Build is running
2023-07-26 20:14:18 +00:00
e957fd8ad6 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.308
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-25 19:05:57 +00:00
032d21d01f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.307
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 20:44:05 +00:00
2f804749d9 chore(deps): update dependency vite to v4.4.7
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 17:11:27 +00:00
d98fd94873 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-24 00:06:10 +00:00
fb427c4ff6 chore(deps): update dependency vite to v4.4.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-22 01:03:41 +02:00
0b6c6e626c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.306 2023-07-22 01:03:10 +02:00
f0d021c1c0 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.305 2023-07-22 01:03:10 +02:00
ff8204ce51 chore(deps): update dependency prettier-plugin-svelte to v3
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-20 08:31:15 +00:00
1d3f78eae7 chore(deps): update typescript-eslint monorepo to v6
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-07-20 08:07:55 +00:00
a6b5e569a3 chore(deps): update dependency prettier to v3
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
2023-07-20 05:08:24 +00:00
b7fb5506f5 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.304
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-20 04:06:29 +00:00
2e1d97395d chore(deps): update module github.com/aws/aws-sdk-go to v1.44.303
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-19 19:06:28 +00:00
058a77cd5a chore(deps): update dependency eslint to v8.45.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-19 14:28:07 +00:00
dcc2135b1f chore(deps): update dependency vite to v4.4.4
Some checks are pending
continuous-integration/drone/push Build is pending
2023-07-19 14:27:49 +00:00
3301d89889 chore(deps): update dependency @sveltejs/kit to v1.22.3
Some checks are pending
continuous-integration/drone/push Build is pending
2023-07-19 14:27:27 +00:00
7ad2d5a61a chore(deps): update github.com/protonmail/go-crypto digest to 5aa5874
Some checks are pending
continuous-integration/drone/push Build is pending
2023-07-19 14:27:12 +00:00
345d3cc6e7 chore(deps): update typescript-eslint monorepo to v5.62.0
Some checks are pending
continuous-integration/drone/push Build is pending
2023-07-19 14:26:55 +00:00
dcf18dc750 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.302
Some checks are pending
continuous-integration/drone/push Build is running
2023-07-18 19:06:55 +00:00
da7e352d0a Migrate to umami v2
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-12 11:00:18 +02:00
41f6681696 chore(deps): update dependency @sveltejs/kit to v1.22.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-11 16:08:57 +00:00
e7bc9e874e chore(deps): update dependency vite to v4.4.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-11 15:06:20 +00:00
db026b8777 chore(deps): update dependency svelte-check to v3.4.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-11 13:06:04 +00:00
22f3c9094b chore(deps): update module github.com/aws/aws-sdk-go to v1.44.299
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-11 05:06:52 +00:00
bc05e807ee chore(deps): update github.com/protonmail/go-crypto digest to e01326f
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-10 12:07:50 +00:00
316b0c9ced chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-10 00:06:50 +00:00
99093aacce chore(deps): update module github.com/aws/aws-sdk-go to v1.44.298
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-09 18:14:11 +00:00
0a38c6a2bb chore(deps): update dependency vite to v4.4.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-09 15:08:41 +00:00
bdbf76cfab chore(deps): update dependency @sveltejs/kit to v1.22.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-06 16:06:02 +00:00
37eb2001bd chore(deps): update dependency svelte-check to v3.4.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-06 14:07:20 +00:00
cc8e7e5885 chore(deps): update dependency vite to v4.4.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-06 09:06:10 +00:00
1377896d9c chore(deps): update module golang.org/x/oauth2 to v0.10.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-05 22:06:36 +00:00
db1aac506c chore(deps): update dependency tslib to v2.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-05 21:27:57 +00:00
e81d835813 chore(deps): update typescript-eslint monorepo to v5.61.0
Some checks are pending
continuous-integration/drone/push Build is pending
2023-07-05 21:27:46 +00:00
d3b1906113 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.296
Some checks are pending
continuous-integration/drone/push Build is running
2023-07-05 19:20:02 +00:00
e31842e37a chore(deps): update dependency @sveltejs/kit to v1.22.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-05 13:19:34 +00:00
cade7bbb2c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.295
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-05 10:09:35 +00:00
e2424bcd0a chore(deps): update dependency eslint to v8.44.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-03 14:07:09 +00:00
4a43fdc7ba chore(deps): update dependency dayjs to v1.11.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-03 09:58:41 +00:00
2f34b2fd86 chore(deps): update dependency typescript to v5.1.6
Some checks are pending
continuous-integration/drone/push Build is running
2023-07-03 09:58:28 +00:00
3e4ce3f783 chore(deps): update typescript-eslint monorepo to v5.60.1
Some checks are pending
continuous-integration/drone/push Build is running
2023-07-03 05:23:54 +00:00
6c2164748f chore(deps): update github.com/protonmail/go-crypto digest to 7e9e039
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-03 04:08:41 +00:00
ce0ec15652 chore(deps): update dependency svelte-check to v3.4.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-25 08:56:00 +00:00
775a012340 chore(deps): update dependency svelte to v3.59.2
Some checks are pending
continuous-integration/drone/push Build is pending
2023-06-25 08:55:42 +00:00
86516e7624 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.289
Some checks are pending
continuous-integration/drone/push Build is pending
2023-06-25 08:55:15 +00:00
b3203fe889 chore(deps): update module golang.org/x/oauth2 to v0.9.0
Some checks are pending
continuous-integration/drone/push Build is pending
2023-06-25 08:54:49 +00:00
c4c7605eb0 chore(deps): update dependency eslint to v8.43.0
Some checks are pending
continuous-integration/drone/push Build is pending
2023-06-25 08:54:38 +00:00
1fe89efd37 chore(deps): update typescript-eslint monorepo to v5.60.0
Some checks are pending
continuous-integration/drone/push Build is pending
2023-06-25 08:54:04 +00:00
bb54322bb1 chore(deps): update dependency @sveltejs/kit to v1.20.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-23 19:06:54 +00:00
e9625f07c4 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.284
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2023-06-16 23:12:50 +00:00
7935145094 chore(deps): update dependency @sveltejs/kit to v1.20.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-16 22:07:50 +00:00
8cecf6aeba chore(deps): update module github.com/aws/aws-sdk-go to v1.44.281
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-12 19:07:00 +00:00
d73d2be0c6 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-12 00:07:55 +00:00
335a7e6229 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.280
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-09 19:06:54 +00:00
f7eed34bdb chore(deps): update typescript-eslint monorepo to v5.59.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-09 07:34:18 +00:00
a3c549d9e8 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.279
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-08 19:06:44 +00:00
9d12fc8b20 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.278
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-07 19:06:36 +00:00
83e180f5a2 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.277
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-06 19:09:16 +00:00
4ce40ca340 chore(deps): update dependency @sveltejs/kit to v1.20.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-06 18:06:28 +00:00
b143a8ddbf chore(deps): update module github.com/aws/aws-sdk-go to v1.44.276
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-05 21:06:47 +00:00
518ccb8cbd chore(deps): update module github.com/gin-gonic/gin to v1.9.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-05 05:08:15 +00:00
14c1dc819e chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-05 00:07:33 +00:00
608da24d48 chore(deps): update dependency eslint to v8.42.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-03 00:06:51 +00:00
f8a4857460 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.275
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-02 19:06:43 +00:00
f4b28e0818 chore(deps): update dependency @sveltejs/kit to v1.20.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-01 23:06:30 +00:00
fb1dcc7b6f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.274
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-01 19:06:34 +00:00
05ca15a234 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.273
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-31 19:06:43 +00:00
384c5a37b9 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.272
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-30 19:06:36 +00:00
b9f7435192 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-29 09:08:47 +00:00
a4512d4ce1 chore(deps): update dependency svelte-check to v3.4.3
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-28 22:09:57 +00:00
aed9baf100 chore(deps): update dependency svelte-preprocess to v5.0.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-28 21:11:16 +00:00
c676c7d462 chore(deps): update dependency prettier-plugin-svelte to v2.10.1
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-28 21:10:58 +00:00
490df34933 chore(deps): update dependency @sveltejs/kit to v1.20.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-28 19:13:45 +00:00
c3c4b32fdf chore(deps): update module github.com/aws/aws-sdk-go to v1.44.271
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-28 17:10:19 +00:00
706294a16c chore(deps): update github.com/protonmail/go-crypto digest to 6f98819
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-28 14:08:31 +00:00
f3fcf0af1f chore(deps): update dependency vite to v4.3.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-28 11:08:52 +00:00
7cb1776f1e chore(deps): update module github.com/aws/aws-sdk-go to v1.44.269
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-24 19:06:31 +00:00
f4a7769b55 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.268
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-23 19:06:26 +00:00
4d34d0fd0f chore(deps): update typescript-eslint monorepo to v5.59.7
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-22 20:07:57 +00:00
f0f8f7a972 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.267
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-22 19:06:58 +00:00
2fa1faf0f3 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-22 04:05:02 +00:00
354f405380 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-22 00:06:58 +00:00
74b8a7d7df chore(deps): update dependency typescript to v5
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-21 08:51:56 +00:00
93df8c4385 chore(deps): update module github.com/coreos/go-oidc/v3 to v3.6.0
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-21 08:51:21 +00:00
d75f56efc8 chore(deps): update dependency eslint to v8.41.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-21 04:08:16 +00:00
d15179372a chore(deps): update dependency @sveltejs/kit to v1.18.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-21 01:08:36 +00:00
5c41db3a9d chore(deps): update module github.com/aws/aws-sdk-go to v1.44.266
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-21 00:08:13 +00:00
466c2f53ae chore(deps): update dependency vite to v4.3.8
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 22:23:33 +00:00
7809d1c738 chore(deps): update github.com/protonmail/go-crypto digest to 7afd394
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 20:06:44 +00:00
e44c09f12b chore(deps): update dependency tslib to v2.5.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-20 17:35:16 +00:00
38a0bab18f chore(deps): update typescript-eslint monorepo to v5.59.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-16 06:57:54 +00:00
b9c30a38fd chore(deps): update module github.com/aws/aws-sdk-go to v1.44.263
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-15 19:06:49 +00:00
73904a88c5 chore(deps): update dependency vite to v4.3.6
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-15 15:06:09 +00:00
01d011e11c chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-15 00:07:09 +00:00
7aa0991a94 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.262
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-11 19:06:21 +00:00
48e4fe2f31 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.261
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-10 19:16:40 +00:00
39b719f9ec chore(deps): update alpine docker tag to v3.18
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-10 00:05:47 +00:00
198f264d77 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.260
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 19:05:49 +00:00
a17835c5af chore(deps): update dependency @sveltejs/kit to v1.16.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 16:05:43 +00:00
b3c2f2adb0 chore(deps): update typescript-eslint monorepo to v5.59.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 09:01:40 +00:00
730c21f4fe chore(deps): update module golang.org/x/oauth2 to v0.8.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 04:05:58 +00:00
57f1beacab chore(deps): update module github.com/aws/aws-sdk-go to v1.44.259
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-08 19:06:23 +00:00
2b14688d6c chore(deps): update dependency svelte-check to v3.3.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-08 16:05:30 +00:00
e81e88367e chore(deps): update dependency svelte to v3.59.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-08 14:05:45 +00:00
e20ceb4701 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-08 00:06:10 +00:00
45d4311046 chore(deps): update dependency eslint to v8.40.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 21:08:19 +00:00
02f6f9d12a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.258
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 20:08:33 +00:00
9d4fd8c3a1 chore(deps): update dependency @sveltejs/kit to v1.16.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 18:08:29 +00:00
4ac8937e0f chore(deps): update dependency vite to v4.3.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 17:06:11 +00:00
f96561998c chore(deps): update dependency svelte to v3.59.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 16:30:10 +00:00
0c17ff1165 chore(deps): update dependency svelte-check to v3.3.1
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-05 15:07:24 +00:00
552fd6ce8a chore(deps): update dependency @sveltejs/kit to v1.16.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 14:06:07 +00:00
7ab55a0793 chore(deps): update dependency @sveltejs/kit to v1.16.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 07:49:33 +00:00
2745dbc6ab renovatebot: Extends from local presets
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-05 09:49:12 +02:00
a86123ffa9 chore(deps): update dependency svelte-check to v3.3.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 06:58:05 +00:00
82d2141811 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.257
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-04 19:14:07 +00:00
975fadf31c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.256
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-04 12:47:03 +00:00
db731e5807 Automerge go-crypto
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-02 11:07:55 +02:00
b4c3577367 chore(deps): update node docker tag to v20
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-02 09:03:46 +00:00
ca9f680f72 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.59.2
Some checks are pending
continuous-integration/drone/push Build is pending
2023-05-02 09:03:31 +00:00
4cc2a677cb chore(deps): update dependency @typescript-eslint/parser to v5.59.2
Some checks are pending
continuous-integration/drone/push Build is pending
2023-05-02 09:03:15 +00:00
ac9a76d0b2 chore(deps): update module github.com/go-sql-driver/mysql to v1.7.1
Some checks are pending
continuous-integration/drone/push Build is pending
2023-05-02 09:02:45 +00:00
1959c50eb1 chore(deps): update github.com/protonmail/go-crypto digest to 58e86b2
Some checks are pending
continuous-integration/drone/push Build is pending
2023-05-02 09:02:33 +00:00
c5f8132a33 chore(deps): update dependency vite to v4.3.4
Some checks are pending
continuous-integration/drone/push Build is running
2023-05-02 08:05:12 +00:00
6b17e03a70 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.254
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-01 19:05:25 +00:00
d0de6af32a chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-01 00:07:04 +00:00
75c8ea9e6c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.253
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 19:05:56 +00:00
c8b491f1e4 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.252
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-27 19:05:47 +00:00
30e59a6fd7 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.251
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-26 19:06:00 +00:00
9b50516bac chore(deps): update module github.com/aws/aws-sdk-go to v1.44.249
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-24 19:05:35 +00:00
08bc95d6a1 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-24 00:06:35 +00:00
e5aa1b6c2f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.248
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-21 19:05:10 +00:00
79814fba21 Ensure other repositories are given as ssh URL
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-21 13:47:18 +02:00
1ab2df8228 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.247
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-20 19:05:16 +00:00
d0d5b835a7 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.246
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-19 21:05:22 +00:00
8a6e5e70f0 chore(deps): update dependency @typescript-eslint/parser to v5.59.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-17 22:05:05 +00:00
59e11e479b chore(deps): update github.com/protonmail/go-crypto digest to 8ee5748
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-17 21:16:10 +00:00
8f20909dc3 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.59.0
Some checks are pending
continuous-integration/drone/push Build is pending
2023-04-17 21:15:24 +00:00
35981edf56 chore(deps): update dependency @sveltejs/kit to v1.15.6
Some checks are pending
continuous-integration/drone/push Build is pending
2023-04-17 21:15:04 +00:00
04f2eceec3 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.245
Some checks are pending
continuous-integration/drone/push Build is pending
2023-04-17 19:05:13 +00:00
c288c962e1 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.244
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-17 08:05:56 +00:00
65fe9f57b4 chore(deps): update dependency @sveltejs/adapter-static to v2.0.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-17 07:44:36 +00:00
a53788f79c chore(deps): update dependency @sveltejs/kit to v1.15.5
Some checks are pending
continuous-integration/drone/push Build is pending
2023-04-17 07:44:24 +00:00
c529036681 chore(deps): update github.com/protonmail/go-crypto digest to 8b3893e
Some checks are pending
continuous-integration/drone/push Build is pending
2023-04-17 04:04:28 +00:00
2f2f80fa1b Fix nil pointer exception
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-11 15:59:29 +02:00
ba6e1490c3 Implement SSH signing retrieval
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-11 13:20:08 +02:00
a2a2f7dc87 chore(deps): update dependency @typescript-eslint/parser to v5.58.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-10 20:09:01 +00:00
b897c4ac68 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.58.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-04-10 18:05:13 +00:00
e6512f3910 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-10 06:08:23 +00:00
b7cdb53c72 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.239
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-10 04:05:43 +00:00
836da2ae30 chore(deps): update dependency svelte-check to v3.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-09 17:47:10 +00:00
db253aec61 chore(deps): update dependency svelte to v3.58.0
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-09 17:46:55 +00:00
3caf5b2597 chore(deps): update dependency eslint to v8.37.0
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-09 17:46:36 +00:00
85c52afba1 chore(deps): update github.com/protonmail/go-crypto digest to d34776a
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-09 17:46:13 +00:00
30edb5c5c6 chore(deps): update dependency @sveltejs/kit to v1.15.2
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-09 17:45:52 +00:00
f4975a7154 chore(deps): update dependency @typescript-eslint/parser to v5.57.1
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-09 17:45:45 +00:00
528a07376a chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.57.1
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-09 17:45:41 +00:00
7b93dedfb7 chore(deps): update module golang.org/x/oauth2 to v0.7.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-07 09:06:13 +00:00
eabb32f88b chore(deps): update module github.com/aws/aws-sdk-go to v1.44.238
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-06 19:05:58 +00:00
df456a0bdb chore(deps): update module github.com/aws/aws-sdk-go to v1.44.237
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-05 20:05:46 +00:00
70e2188bb2 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.236
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-04 19:05:43 +00:00
e0a9b85583 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.235
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-03 19:05:37 +00:00
9beeb5d8ff chore(deps): update module github.com/aws/aws-sdk-go to v1.44.234
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-03 07:07:14 +00:00
ee93d9de41 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-27 00:07:04 +00:00
255065e466 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.228
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-23 20:06:24 +00:00
006727a97a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.227
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-22 19:07:47 +00:00
1516566376 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.226
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-21 19:06:30 +00:00
9e23e7a563 chore(deps): update github.com/protonmail/go-crypto digest to 8451524
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-20 23:08:24 +00:00
cb86fc8937 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.225
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-20 22:07:36 +00:00
65720a682b chore(deps): update github.com/protonmail/go-crypto digest to cb82d93
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-03-18 09:06:33 +00:00
d1649b5b4f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.224
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-17 20:06:37 +00:00
28a162c19e chore(deps): update module github.com/aws/aws-sdk-go to v1.44.223
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-16 23:07:58 +00:00
66d7778c10 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.222
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-15 23:07:52 +00:00
fe15719850 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.221
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-15 02:06:32 +00:00
59f3cdb52a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.220
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-13 19:06:40 +00:00
0d6e546399 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.219
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-13 08:06:05 +00:00
3750c30d99 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.218
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-09 22:06:48 +00:00
19a759536b Add grade filter by category
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-08 17:13:55 +01:00
9ad021fa65 Fix redogradation in WorkGradesSteps
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-08 17:01:45 +01:00
49dd674f72 User trace report can contains messages 2023-03-08 17:01:45 +01:00
1348678be1 Don't rely on a fixed number of line to hide, expect first command 2023-03-08 17:01:45 +01:00
0965698c90 New route and button to anonymize old users 2023-03-08 17:01:45 +01:00
a9bb758e99 Include works in allGrades 2023-03-08 17:01:45 +01:00
93b5857fa6 Go to grades promotion page directly 2023-03-08 04:22:45 +01:00
2ee8901d8c Detail tests with "report"s
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-08 04:15:17 +01:00
e90d55c07e Also display "Clean archive" and "Has xxx"
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-08 00:06:05 +01:00
522fe05e99 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.216
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-07 20:06:44 +00:00
9032d53ae3 Always show a button to launch gradation, even in case of error
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-07 17:38:12 +01:00
61dde4e7e3 Can edit grade's commentary
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-07 04:53:50 +01:00
23aa993671 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.215
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-07 04:48:53 +01:00
3af70cf678 Can see directly the corresponding trace 2023-03-07 04:45:04 +01:00
1f4ce865aa New page to see grades sumary 2023-03-07 04:41:02 +01:00
7642a23947 New form to change grade 2023-03-07 03:37:06 +01:00
b604e98f64 New parameter to display a message at the top of the screen 2023-03-07 03:36:27 +01:00
f7da603dbe New UpTo parameter to reduce grade accordingly 2023-03-07 03:36:27 +01:00
07ceb20d63 Refactor work page 2023-03-07 03:36:25 +01:00
6c471b5ec1 Add links to the forge 2023-03-06 18:46:41 +01:00
562cd5b397 Hide repository infos when corrected 2023-03-06 18:24:43 +01:00
6fed13ee0e Also accepts step key to be step name
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 18:19:11 +01:00
b1ad293810 Expect avant-last line to be + echo grade 2023-03-06 18:18:52 +01:00
2129ba46ee Apply user when redogradation
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 15:55:30 +01:00
18225ca1d6 Also display builds
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-06 15:32:52 +01:00
0836486e3e Don't launch gradation for 404 submission user
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 15:01:39 +01:00
db5658ccc1 Distribute some traces to students
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-06 14:58:37 +01:00
018ed9227f Fetch gradation status 2023-03-06 14:58:36 +01:00
1f7896ff26 Don't consider missing submissions in min grade
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-06 13:15:55 +01:00
5599a94f18 New button to redo gradation
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-06 13:13:04 +01:00
cd500afbd5 Add button to go to the student's repository 2023-03-06 13:08:58 +01:00
3397b9f123 Gradation: add route to add missing users 2023-03-06 13:08:58 +01:00
107b17c11f AddGrade erase any previous grade recorded for the user
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-06 12:37:34 +01:00
51a311f0b9 ui: Fix race condition where refresh can occurs before deletion 2023-03-06 12:37:34 +01:00
0b192e4783 ui: Add more grade stats 2023-03-06 12:37:34 +01:00
cc729227ce ui: Auto display grades to admin when work is over 2023-03-06 12:37:33 +01:00
6cd3907dc3 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 04:05:09 +00:00
184daa4cab New route to retrieve gradation status
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 03:44:39 +01:00
1515140c09 Add a default value to testsref
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 03:17:45 +01:00
9a145965fb ui: Change Id to ensure they are all differents 2023-03-06 03:17:45 +01:00
bcf76a2c86 New route to stop all running/pending tests for a given work 2023-03-06 03:17:45 +01:00
8b8f3947f8 Refactor gradation + add way to give point for a succeeded task 2023-03-06 03:17:45 +01:00
a78de73671 ui: Display a spinner during gradation launch 2023-03-06 03:17:45 +01:00
906501cc7b ui: Use ScoreBadge component 2023-03-06 03:17:45 +01:00
5020f378c8 Only consider the last log line 2023-03-06 03:17:45 +01:00
ce0c8e0025 Update SharingTime to 20 min as 15 is not enough 2023-03-06 03:17:45 +01:00
64eb8f2852 ui: Add a button to refresh grades 2023-03-06 03:17:45 +01:00
0620d9d834 chore(deps): update module golang.org/x/oauth2 to v0.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-05 14:06:33 +00:00
61f4795511 New route to view user's trace through repository
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-05 14:41:09 +01:00
084244449c New route to display user's traces 2023-03-05 14:41:09 +01:00
706e786190 New route to delete grades 2023-03-05 14:41:09 +01:00
6f9b83ef24 ui: New button to launch all tests 2023-03-05 14:41:09 +01:00
936a8a80f4 New callback to compute grade after tests 2023-03-05 14:41:09 +01:00
91aee60bfb Take int instead of int64 2023-03-05 03:57:37 +01:00
e57b7a7089 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.214
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-04 16:49:27 +01:00
e61a8bd51d lives: Can display results as charts on all screens 2023-03-04 16:49:27 +01:00
5b2fddddc1 lives: Stop existing timer when changing mind 2023-03-04 16:49:27 +01:00
ce1d61b6c4 ui: Also color score on works page 2023-03-04 16:49:27 +01:00
99a1e3c111 Don't force current promo when loggin via kerberos 2023-03-04 16:49:27 +01:00
8b1d7cc5fc chore(deps): update module github.com/aws/aws-sdk-go to v1.44.213 2023-03-04 16:49:27 +01:00
88f47cbae9 chore(deps): update module github.com/jcmturner/gokrb5/v8 to v8.4.4 2023-03-04 16:49:27 +01:00
4987ecd174 chore(deps): update module github.com/gin-gonic/gin to v1.9.0 2023-03-04 16:49:27 +01:00
61ce4e5c1d chore(deps): update module github.com/aws/aws-sdk-go to v1.44.212 2023-03-04 16:49:27 +01:00
4fb7b768e1 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.211 2023-03-04 16:49:27 +01:00
9bb8fe513a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.210 2023-03-04 16:49:27 +01:00
6478b2fce8 chore(deps): lock file maintenance 2023-03-04 16:49:27 +01:00
2a45835a08 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.209 2023-03-04 16:49:27 +01:00
e7cadf67f3 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.208 2023-03-04 16:49:27 +01:00
0afce9a789 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.207 2023-03-04 16:49:27 +01:00
020f171e36 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.206 2023-03-04 16:49:26 +01:00
6f332b2e2f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.205 2023-03-04 16:49:26 +01:00
f3f7c55be3 chore(deps): lock file maintenance 2023-03-04 16:49:26 +01:00
e7a7ad407c chore(deps): update github.com/protonmail/go-crypto digest to 7d5c6f0 2023-03-04 16:49:26 +01:00
460b1a9381 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.204 2023-03-04 16:49:26 +01:00
3e839be0db chore(deps): update module github.com/aws/aws-sdk-go to v1.44.201 2023-03-04 16:49:26 +01:00
04fba5b199 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.200 2023-03-04 16:49:26 +01:00
8279b1dc98 chore(deps): lock file maintenance 2023-03-04 16:49:26 +01:00
db203f8031 Remove need help menu item 2023-03-04 16:49:26 +01:00
5ab23024b6 Redirect to the course of the year when promo is different 2023-03-04 16:49:26 +01:00
9670861e3f renovate: Enable lock-file maintenance 2023-03-04 16:49:26 +01:00
e90705562e chore(deps): update module github.com/aws/aws-sdk-go to v1.44.199 2023-03-04 16:49:26 +01:00
1f632ed381 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.198 2023-03-04 16:49:26 +01:00
8c64ac6bab chore(deps): update dependency @sveltejs/adapter-static to v2 2023-03-04 16:49:26 +01:00
898dfde3ad chore(deps): update module golang.org/x/oauth2 to v0.5.0 2023-03-04 16:49:26 +01:00
96b0df19fa chore(deps): update module github.com/aws/aws-sdk-go to v1.44.197 2023-03-04 16:49:26 +01:00
b76dc7ec9f chore(deps): update module github.com/aws/aws-sdk-go to v1.44.196 2023-03-04 16:49:26 +01:00
4045308694 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.195 2023-03-04 16:49:26 +01:00
d54e78874a chore(deps): update github.com/protonmail/go-crypto digest to d1d05f4 2023-03-04 16:49:26 +01:00
87f23aa3fe chore(deps): update module github.com/aws/aws-sdk-go to v1.44.194 2023-03-04 16:49:26 +01:00
09298727e6 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.193 2023-03-04 16:49:26 +01:00
994ef52a0c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.191 2023-03-04 16:49:26 +01:00
6fb8ea3ae5 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.190 2023-03-04 16:49:26 +01:00
58ebeb3272 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.189 2023-03-04 16:49:26 +01:00
8f367ddffe chore(deps): update module github.com/aws/aws-sdk-go to v1.44.188 2023-03-04 16:49:26 +01:00
c09103fd2c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.187 2023-03-04 16:49:26 +01:00
d435dfb46c chore(deps): update github.com/protonmail/go-crypto digest to 0acdc8a 2023-03-04 16:49:26 +01:00
3924415998 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.186 2023-03-04 16:49:26 +01:00
f0986e7c83 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.185 2023-03-04 16:49:26 +01:00
9cdc7fd7d6 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.184 2023-03-04 16:49:26 +01:00
f57ab66f0c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.183 2023-03-04 16:49:26 +01:00
7fd2b29551 chore(deps): update github.com/protonmail/go-crypto digest to a47887b 2023-03-04 16:49:26 +01:00
f5d96a6837 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.182 2023-03-04 16:49:26 +01:00
9353de70e1 Fix build on arm 2023-03-04 16:49:26 +01:00
9e9d31f850 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.180 2023-03-04 16:49:26 +01:00
fd07d9e434 chore(deps): update github.com/protonmail/go-crypto digest to 068501e 2023-03-04 16:49:26 +01:00
4bb801ca37 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.179 2023-03-04 16:49:26 +01:00
24d44eaa61 chore(deps): update github.com/protonmail/go-crypto digest to 7efeeb0 2023-03-04 16:49:26 +01:00
74292ffff3 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.178 2023-03-04 16:49:26 +01:00
89cfee335a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.177 2023-03-04 16:49:26 +01:00
77905b912b chore(deps): update module github.com/aws/aws-sdk-go to v1.44.176 2023-03-04 16:49:26 +01:00
d1102923a5 chore(deps): update github.com/protonmail/go-crypto digest to cdd2c18 2023-03-04 16:49:26 +01:00
0026e4ebd2 chore(deps): update module github.com/coreos/go-oidc/v3 to v3.5.0 2023-03-04 16:49:26 +01:00
99b7fad056 chore(deps): update module golang.org/x/oauth2 to v0.4.0 2023-03-04 16:49:26 +01:00
9e956edb57 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.175 2023-03-04 16:49:26 +01:00
735a120259 New route to run gradation tests 2023-03-04 16:49:26 +01:00
7340e10a7a Add fields for tests 2023-01-01 19:32:32 +01:00
ec71073cb6 Change date field size
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-01 16:11:33 +01:00
6def3de983 New field added for works to store gradation repository
Some checks are pending
continuous-integration/drone/push Build is running
2023-01-01 15:26:58 +01:00
e8d34869cc chore(deps): update module github.com/aws/aws-sdk-go to v1.44.171
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-30 20:06:50 +00:00
716aa39827 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.170
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-29 20:07:49 +00:00
14afe26e5a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.169
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 20:07:18 +00:00
5b528e4e89 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.168
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-27 20:07:16 +00:00
9089e4877d chore(deps): update module github.com/aws/aws-sdk-go to v1.44.167
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-23 20:08:32 +00:00
95c221ae49 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.166
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-22 20:07:16 +00:00
e069a29292 chore(deps): update module github.com/gin-gonic/gin to v1.8.2
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-22 16:58:55 +00:00
366768abde chore(deps): update dependency svelte-check to v3
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-22 12:06:30 +00:00
3063b1aa88 Update module github.com/aws/aws-sdk-go to v1.44.165
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 21:07:12 +00:00
f592b8a1b5 Fix typo
All checks were successful
continuous-integration/drone/push Build is passing
Thanks-to: Pierre-Olivier Rey <pierre-olivier.rey@epita.fr>
2022-12-21 10:14:44 +01:00
47121bb3d3 Update module github.com/aws/aws-sdk-go to v1.44.164
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 20:06:06 +00:00
289702a341 Update module github.com/aws/aws-sdk-go to v1.44.163
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 20:06:11 +00:00
8ed2526f97 Add tooltips on survey badges
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 15:36:19 +01:00
96466dedf7 Fix a recurrent survey reloading 2022-12-19 15:36:14 +01:00
24c53afad6 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.162
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-16 20:06:27 +00:00
c36119d49a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.161
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-15 20:06:05 +00:00
dde1f825cc ui: Update git remote
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-15 17:33:41 +01:00
c692e34fdf Update to sveltekit 1.0 + fix warnings
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-15 15:17:03 +01:00
ba5b8570b7 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.160
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-14 20:05:49 +00:00
78ea97d1d4 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.159
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 20:05:52 +00:00
0bcedc61d9 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.158
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 12:49:30 +01:00
e9c450742b Update node modules 2022-12-13 12:48:59 +01:00
ff5a2eef65 svelte-migrate: updated files 2022-12-13 12:48:59 +01:00
4d6149760d svelte-migrate: renamed files 2022-12-12 08:49:28 +01:00
6d9cbdf048 chore(deps): update dependency svelte-preprocess to v5
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-11 22:18:19 +00:00
34d5ddfbaa chore(deps): update dependency vite to v4
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-11 22:16:05 +00:00
1ce2906d72 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.157
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-09 20:06:41 +00:00
a969508aa3 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.156
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-08 20:06:19 +00:00
546cbf9ab5 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.155
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-07 20:05:43 +00:00
c69d6bf6db chore(deps): update module github.com/aws/aws-sdk-go to v1.44.154
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-06 20:05:38 +00:00
c93e644288 chore(deps): update module github.com/go-sql-driver/mysql to v1.7.0
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-06 17:27:15 +00:00
5b81ca412b chore(deps): update module golang.org/x/oauth2 to v0.3.0
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-06 16:06:06 +00:00
f2ab869259 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.153
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-05 20:05:43 +00:00
9d8981456e chore(deps): update module github.com/aws/aws-sdk-go to v1.44.152
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-05 12:06:25 +00:00
fff8b821c5 Can share survey results with a secret shared key
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-02 12:43:53 +01:00
9fd73ce235 Refactor user access check to questions 2022-12-02 11:48:53 +01:00
ad0d12e67a chore(deps): update module github.com/aws/aws-sdk-go to v1.44.151
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-01 21:05:35 +00:00
9102eae561 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.150
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-30 20:07:03 +00:00
3b716d73c7 Ensure URL format of git repositories
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-30 05:15:18 +01:00
c5a75b6fa8 Can utilize a custom repository
Some checks are pending
continuous-integration/drone/push Build is running
2022-11-30 05:00:46 +01:00
492dd16ed9 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.149
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-29 23:05:17 +00:00
e345e967d6 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.148
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-29 21:05:10 +00:00
f9ba0c0a60 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.147
Some checks are pending
continuous-integration/drone/push Build is running
2022-11-29 06:07:03 +00:00
2fc35c1a75 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.146
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-28 03:07:40 +00:00
c7059671cb Add a button to export list questions
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-28 00:32:09 +01:00
8f5e2f380f Make survey duplication synchronous (to avoid proposals randomization)
Some checks are pending
continuous-integration/drone/push Build is running
2022-11-28 00:25:26 +01:00
0cab193d6c New view to aggregate survey responses
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-26 16:04:17 +01:00
4be5503af9 ui: When no corrections have been made, juste display nb 2022-11-26 16:04:17 +01:00
749d55c24f Handle no response in survey list 2022-11-26 16:04:17 +01:00
ee4872ff38 Update module github.com/aws/aws-sdk-go to v1.44.145
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-23 20:07:07 +00:00
1c7803169d Update module github.com/aws/aws-sdk-go to v1.44.144
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-22 20:07:05 +00:00
b4ef09dd5b Update module github.com/aws/aws-sdk-go to v1.44.143
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-21 20:07:05 +00:00
ce6141e6e0 Make rendus page more interactive
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-20 15:53:50 +01:00
4c76dd9728 Add categories to sort/filter works/surveys
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-20 15:28:27 +01:00
b88d284859 Round score
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-20 13:30:51 +01:00
f7d747cd76 In corrections, highlight significant texts
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-19 18:48:05 +01:00
6a54315626 Display number of items left to correct to admin on surveylist 2022-11-19 17:31:48 +01:00
1532ede587 Revert "Display survey mean to admin"
All checks were successful
continuous-integration/drone/push Build is passing
This reverts commit d7f679ce84.
2022-11-19 13:05:26 +01:00
c21df9d93f Credit François on bounty page
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-19 11:44:47 +01:00
f675047ce8 Refactor permissions checks to avoid questions/works leaks between promotions/groups/start-availability
Thanks-To François Dautrême <francois.dautreme@epita.fr>
2022-11-19 11:44:31 +01:00
bf5b0e88dd Fix correction template mess 2022-11-19 11:44:31 +01:00
d7f679ce84 Display survey mean to admin 2022-11-19 11:44:31 +01:00
d6f620bc0d ui: Use $lib in imports 2022-11-19 11:44:31 +01:00
10d0a6a836 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.142
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-18 23:05:14 +00:00
4ebcc890dc chore(deps): update module github.com/aws/aws-sdk-go to v1.44.141
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-18 20:07:00 +00:00
55700ce103 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.140
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-17 20:05:14 +00:00
4bf8cce899 chore(deps): update alpine docker tag to v3.17
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-16 22:04:40 +00:00
5cc07a4c70 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.139
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-16 20:07:05 +00:00
140935e15d Don't validate answer without valid correction template
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-16 09:37:56 +01:00
8fd9908d59 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.138
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-15 20:05:05 +00:00
12d531873c chore(deps): update module github.com/aws/aws-sdk-go to v1.44.137
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-14 20:04:55 +00:00
b6d17c2eb2 chore(deps): update module github.com/aws/aws-sdk-go to v1.44.136
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-11 21:07:05 +00:00
b4b531409f Implement optional signature
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-11 13:27:09 +01:00
132 changed files with 8615 additions and 5859 deletions

View file

@ -1,56 +1,6 @@
---
kind: pipeline
type: docker
name: build-arm
platform:
os: linux
arch: arm
steps:
- name: build front
image: node:19-alpine
commands:
- mkdir deploy
- cd ui
- npm install --network-timeout=100000
- npm run build
- tar chjf ../deploy/static.tar.bz2 build
- name: vet
image: golang:1-alpine
commands:
- apk --no-cache add build-base
- go vet -v -buildvcs=false
- name: backend armv7
image: golang:1-alpine
commands:
- apk --no-cache add build-base
- go get -v
- go build -v -buildvcs=false -ldflags="-s -w"
environment:
GOARM: 7
- name: publish
image: plugins/docker
settings:
repo: nemunaire/atsebay.t
auto_tag: true
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
username:
from_secret: docker_username
password:
from_secret: docker_password
trigger:
event:
- cron
- push
- tag
---
kind: pipeline
type: docker
name: build-arm64
platform:
@ -59,7 +9,7 @@ platform:
steps:
- name: build front
image: node:19-alpine
image: node:21-alpine
commands:
- mkdir deploy
- cd ui
@ -119,5 +69,4 @@ trigger:
- tag
depends_on:
- build-arm
- build-arm64

View file

@ -1,4 +1,4 @@
FROM node:19-alpine as nodebuild
FROM node:21-alpine as nodebuild
WORKDIR /ui
@ -22,7 +22,7 @@ RUN go get -d -v && \
go build -v -buildvcs=false -ldflags="-s -w" -o atsebay.t
FROM alpine:3.16
FROM alpine:3.19
EXPOSE 8081

4
api.go
View file

@ -13,9 +13,11 @@ func declareAPIRoutes(router *gin.Engine) {
apiRoutes.Use(authMiddleware())
declareAPIAuthRoutes(apiRoutes)
declareAPICategoriesRoutes(apiRoutes)
declareAPISurveysRoutes(apiRoutes)
declareAPIWorksRoutes(apiRoutes)
declareAPIKeysRoutes(apiRoutes)
declareAPISharesRoutes(apiRoutes)
declareCallbacksRoutes(apiRoutes)
authRoutes := router.Group("")
@ -50,7 +52,9 @@ func declareAPIRoutes(router *gin.Engine) {
declareAPIAdminAuthRoutes(apiAdminRoutes)
declareAPIAdminAsksRoutes(apiAdminRoutes)
declareAPIAdminCategoriesRoutes(apiRoutes)
declareAPIAuthGradesRoutes(apiAdminRoutes)
declareAPIAdminGradationRoutes(apiAdminRoutes)
declareAPIAdminHelpRoutes(apiAdminRoutes)
declareAPIAdminQuestionsRoutes(apiAdminRoutes)
declareAPIAuthRepositoriesRoutes(apiAdminRoutes)

View file

@ -10,7 +10,7 @@ import (
"net/http"
)
//go:embed ui/build/* ui/build/css/* ui/build/surveys/* ui/build/_app/* ui/build/_app/immutable/* ui/build/_app/immutable/pages/* ui/build/_app/immutable/pages/surveys/* ui/build/_app/immutable/pages/surveys/_sid_/* ui/build/_app/immutable/pages/surveys/_sid_/responses/* ui/build/_app/immutable/pages/grades/* ui/build/_app/immutable/pages/works/* ui/build/_app/immutable/pages/works/_wid_/* ui/build/_app/immutable/pages/users/* ui/build/_app/immutable/pages/users/_uid_/* ui/build/_app/immutable/pages/users/_uid_/surveys/* ui/build/_app/immutable/chunks/* ui/build/_app/immutable/assets/* ui/build/img/* ui/build/works/*
//go:embed all:ui/build
var _assets embed.FS
var Assets http.FileSystem

29
auth.go
View file

@ -13,6 +13,7 @@ import (
var LocalAuthFunc = checkAuthKrb5
var allowLocalAuth bool
var localAuthUsers arrayFlags
var mainBanner string
type loginForm struct {
Login string `json:"username"`
@ -47,16 +48,18 @@ func declareAPIAdminAuthRoutes(router *gin.RouterGroup) {
session.Update()
c.JSON(http.StatusOK, authToken{
User: newuser,
CurrentPromo: currentPromo,
User: newuser,
CurrentPromo: currentPromo,
MessageBanner: mainBanner,
})
})
}
type authToken struct {
*User
CurrentPromo uint `json:"current_promo"`
Groups []string `json:"groups"`
CurrentPromo uint `json:"current_promo"`
Groups []string `json:"groups"`
MessageBanner string `json:"banner,omitempty"`
}
func validateAuthToken(c *gin.Context) {
@ -64,7 +67,7 @@ func validateAuthToken(c *gin.Context) {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": "Not connected"})
return
} else {
t := authToken{User: u.(*User), CurrentPromo: currentPromo}
t := authToken{User: u.(*User), CurrentPromo: currentPromo, MessageBanner: mainBanner}
t.Groups = strings.Split(strings.TrimFunc(t.User.Groups, func(r rune) bool { return !unicode.IsLetter(r) }), ",")
@ -77,7 +80,7 @@ func logout(c *gin.Context) {
c.JSON(http.StatusOK, true)
}
func completeAuth(c *gin.Context, username string, email string, firstname string, lastname string, promo uint, groups string, face_url string, session *Session) (usr *User, err error) {
func completeAuth(c *gin.Context, username string, email string, firstname string, lastname string, promo uint, groups string, session *Session) (usr *User, err error) {
if !userExists(username) {
if promo == 0 {
promo = currentPromo
@ -114,14 +117,10 @@ func completeAuth(c *gin.Context, username string, email string, firstname strin
if session == nil {
session, err = usr.NewSession()
if err != nil {
return
}
} else {
_, err = session.SetUser(usr)
}
if face_url != "" {
session.SetKey("picture", face_url)
}
_, err = session.SetUser(usr)
if err != nil {
return
}
@ -157,10 +156,10 @@ func dummyAuth(c *gin.Context) {
return
}
if usr, err := completeAuth(c, lf["username"], lf["email"], lf["firstname"], lf["lastname"], currentPromo, "", "", nil); err != nil {
if usr, err := completeAuth(c, lf["username"], lf["email"], lf["firstname"], lf["lastname"], currentPromo, "", nil); err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": err.Error()})
return
} else {
c.JSON(http.StatusOK, authToken{User: usr, CurrentPromo: currentPromo})
c.JSON(http.StatusOK, authToken{User: usr, CurrentPromo: currentPromo, MessageBanner: mainBanner})
}
}

View file

@ -83,7 +83,7 @@ func checkAuthKrb5(c *gin.Context) {
return
}
if usr, err := completeAuth(c, lf.Login, lf.Login+"@epita.fr", "", "", currentPromo, "", "", nil); err != nil {
if usr, err := completeAuth(c, lf.Login, lf.Login+"@epita.fr", "", "", 0, "", nil); err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": err.Error()})
return
} else {

View file

@ -48,7 +48,7 @@ func initializeOIDC(router *gin.Engine) {
Endpoint: provider.Endpoint(),
// "openid" is a required scope for OpenID Connect flows.
Scopes: []string{oidc.ScopeOpenID, "profile", "email", "epita", "picture"},
Scopes: []string{oidc.ScopeOpenID, "profile", "email", "epita"},
}
oidcConfig := oidc.Config{
@ -112,9 +112,6 @@ func OIDC_CRI_complete(c *gin.Context) {
Groups []map[string]interface{} `json:"groups"`
Campuses []string `json:"campuses"`
GraduationYears []uint `json:"graduation_years"`
Picture string `json:"picture"`
PictureSquare string `json:"picture_square"`
PictureThumb string `json:"picture_thumb"`
}
if err := idToken.Claims(&claims); err != nil {
log.Println("Unable to extract claims to Claims:", err.Error())
@ -138,7 +135,7 @@ func OIDC_CRI_complete(c *gin.Context) {
}
}
if _, err := completeAuth(c, claims.Username, claims.Email, claims.Firstname, claims.Lastname, promo, groups, claims.PictureSquare, session); err != nil {
if _, err := completeAuth(c, claims.Username, claims.Email, claims.Firstname, claims.Lastname, promo, groups, session); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}

168
categories.go Normal file
View file

@ -0,0 +1,168 @@
package main
import (
"fmt"
"log"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func declareAPICategoriesRoutes(router *gin.RouterGroup) {
categoriesRoutes := router.Group("/categories/:cid")
categoriesRoutes.Use(categoryHandler)
categoriesRoutes.GET("", func(c *gin.Context) {
c.JSON(http.StatusOK, c.MustGet("category").(*Category))
})
}
func declareAPIAdminCategoriesRoutes(router *gin.RouterGroup) {
router.GET("categories", func(c *gin.Context) {
categories, err := getCategories()
if err != nil {
log.Println("Unable to getCategories:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve categories. Please try again later."})
return
}
c.JSON(http.StatusOK, categories)
})
router.POST("categories", func(c *gin.Context) {
var new Category
if err := c.ShouldBindJSON(&new); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
if new.Promo == 0 {
new.Promo = currentPromo
}
if cat, err := NewCategory(new.Label, new.Promo, new.Expand); err != nil {
log.Println("Unable to NewCategory:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during category creation: %s", err.Error())})
return
} else {
c.JSON(http.StatusOK, cat)
}
})
categoriesRoutes := router.Group("/categories/:cid")
categoriesRoutes.Use(categoryHandler)
categoriesRoutes.PUT("", func(c *gin.Context) {
current := c.MustGet("category").(*Category)
var new Category
if err := c.ShouldBindJSON(&new); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
new.Id = current.Id
if _, err := new.Update(); err != nil {
log.Println("Unable to Update category:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to update the given category. Please try again later."})
return
} else {
c.JSON(http.StatusOK, new)
}
})
categoriesRoutes.DELETE("", func(c *gin.Context) {
current := c.MustGet("category").(*Category)
if _, err := current.Delete(); err != nil {
log.Println("Unable to Delete category:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to delete the given category. Please try again later."})
return
} else {
c.JSON(http.StatusOK, nil)
}
})
}
func categoryHandler(c *gin.Context) {
var category *Category
cid, err := strconv.Atoi(string(c.Param("cid")))
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad category identifier."})
return
} else {
category, err = getCategory(cid)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Category not found."})
return
}
}
c.Set("category", category)
c.Next()
}
type Category struct {
Id int64 `json:"id"`
Label string `json:"label"`
Promo uint `json:"promo"`
Expand bool `json:"expand,omitempty"`
}
func getCategories() (categories []Category, err error) {
if rows, errr := DBQuery("SELECT id_category, label, promo, expand FROM categories ORDER BY promo DESC, expand DESC, id_category DESC"); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var c Category
if err = rows.Scan(&c.Id, &c.Label, &c.Promo, &c.Expand); err != nil {
return
}
categories = append(categories, c)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func getCategory(id int) (c *Category, err error) {
c = new(Category)
err = DBQueryRow("SELECT id_category, label, promo, expand FROM categories WHERE id_category=?", id).Scan(&c.Id, &c.Label, &c.Promo, &c.Expand)
return
}
func NewCategory(label string, promo uint, expand bool) (*Category, error) {
if res, err := DBExec("INSERT INTO categories (label, promo, expand) VALUES (?, ?, ?)", label, promo, expand); err != nil {
return nil, err
} else if cid, err := res.LastInsertId(); err != nil {
return nil, err
} else {
return &Category{cid, label, promo, expand}, nil
}
}
func (c *Category) Update() (int64, error) {
if res, err := DBExec("UPDATE categories SET label = ?, promo = ?, expand = ? WHERE id_category = ?", c.Label, c.Promo, c.Expand, c.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
func (c *Category) Delete() (int64, error) {
if res, err := DBExec("DELETE FROM categories WHERE id_category = ?", c.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}

36
db.go
View file

@ -93,6 +93,7 @@ CREATE TABLE IF NOT EXISTS user_keys(
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS surveys(
id_survey INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_category INTEGER NOT NULL,
title VARCHAR(255),
promo MEDIUMINT NOT NULL,
grp VARCHAR(255) NOT NULL,
@ -100,7 +101,8 @@ CREATE TABLE IF NOT EXISTS surveys(
direct INTEGER DEFAULT NULL,
corrected BOOLEAN NOT NULL DEFAULT FALSE,
start_availability TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
end_availability TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
end_availability TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(id_category) REFERENCES categories(id_category)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
@ -200,6 +202,7 @@ CREATE TABLE IF NOT EXISTS user_need_help(
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS works(
id_work INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_category INTEGER NOT NULL,
title VARCHAR(255),
promo MEDIUMINT NOT NULL,
grp VARCHAR(255) NOT NULL,
@ -207,9 +210,11 @@ CREATE TABLE IF NOT EXISTS works(
description TEXT NOT NULL,
tag VARCHAR(255) NOT NULL,
submission_URL VARCHAR(255) NULL,
gradation_repo VARCHAR(255) NULL,
corrected BOOLEAN NOT NULL DEFAULT FALSE,
start_availability TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
end_availability TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
end_availability TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(id_category) REFERENCES categories(id_category)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
@ -237,6 +242,8 @@ CREATE TABLE IF NOT EXISTS user_work_repositories(
secret BLOB NOT NULL,
last_check TIMESTAMP NULL DEFAULT NULL,
droneref VARCHAR(255) NOT NULL,
last_tests TIMESTAMP NULL DEFAULT NULL,
testsref VARCHAR(255) NOT NULL,
FOREIGN KEY(id_user) REFERENCES users(id_user),
FOREIGN KEY(id_work) REFERENCES works(id_work),
UNIQUE one_repo_per_work (id_user, id_work)
@ -245,12 +252,33 @@ CREATE TABLE IF NOT EXISTS user_work_repositories(
return err
}
if _, err := db.Exec(`
CREATE VIEW IF NOT EXISTS student_scores AS SELECT T.id_user, T.id_survey, Q.id_question, MAX(R.score) AS score FROM (SELECT DISTINCT R.id_user, S.id_survey FROM survey_responses R INNER JOIN survey_quests Q ON R.id_question = Q.id_question INNER JOIN surveys S ON Q.id_survey = S.id_survey) T LEFT OUTER JOIN survey_quests Q ON T.id_survey = Q.id_survey LEFT OUTER JOIN survey_responses R ON R.id_user = T.id_user AND Q.id_question = R.id_question GROUP BY id_user, id_survey, id_question;
CREATE TABLE IF NOT EXISTS categories(
id_category INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
label VARCHAR(255) NOT NULL,
promo MEDIUMINT NOT NULL,
expand BOOLEAN NOT NULL DEFAULT FALSE
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE VIEW IF NOT EXISTS all_works AS SELECT "work" AS kind, id_work AS id, title, promo, grp, shown, NULL AS direct, submission_url, corrected, start_availability, end_availability FROM works UNION SELECT "survey" AS kind, id_survey AS id, title, promo, grp, shown, direct, NULL AS submission_url, corrected, start_availability, end_availability FROM surveys;
CREATE TABLE IF NOT EXISTS survey_shared(
id_share INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_survey INTEGER NOT NULL,
secret BLOB NOT NULL,
count MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
FOREIGN KEY(id_survey) REFERENCES surveys(id_survey)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE VIEW IF NOT EXISTS student_scores AS SELECT "survey" AS kind, T.id_user, T.id_survey AS id, Q.id_question, MAX(R.score) AS score FROM (SELECT DISTINCT R.id_user, S.id_survey FROM survey_responses R INNER JOIN survey_quests Q ON R.id_question = Q.id_question INNER JOIN surveys S ON Q.id_survey = S.id_survey) T LEFT OUTER JOIN survey_quests Q ON T.id_survey = Q.id_survey LEFT OUTER JOIN survey_responses R ON R.id_user = T.id_user AND Q.id_question = R.id_question GROUP BY id_user, kind, id, id_question UNION SELECT "work" AS kind, G.id_user, G.id_work AS id, 0 AS id_question, G.grade AS score FROM works W RIGHT OUTER JOIN user_work_grades G ON G.id_work = W.id_work GROUP BY id_user, kind, id, id_question;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE VIEW IF NOT EXISTS all_works AS SELECT "work" AS kind, id_work AS id, id_category, title, promo, grp, shown, NULL AS direct, submission_url, corrected, start_availability, end_availability FROM works UNION SELECT "survey" AS kind, id_survey AS id, id_category, title, promo, grp, shown, direct, NULL AS submission_url, corrected, start_availability, end_availability FROM surveys;
`); err != nil {
return err
}

View file

@ -2,8 +2,10 @@ package main
import (
"context"
"fmt"
"log"
"net/http"
"strings"
"sync"
"time"
@ -280,6 +282,62 @@ func getCorrectionString(qid int64) (ret map[string]int) {
return
}
func getResponsesStats(qid int64) map[string]interface{} {
q, err := getQuestion(int(qid))
if err != nil {
return nil
}
responses, err := q.GetResponses()
if err != nil {
log.Println("Unable to retrieve responses:", err)
return nil
}
labels := []string{}
values := []uint{}
if q.Kind == "mcq" || q.Kind == "ucq" {
proposals, err := q.GetProposals()
if err != nil {
return nil
}
proposal_idx := map[string]int{}
for _, p := range proposals {
proposal_idx[fmt.Sprintf("%d", p.Id)] = len(labels)
labels = append(labels, p.Label)
values = append(values, 0)
}
for _, r := range responses {
for _, v := range strings.Split(r.Answer, ",") {
values[proposal_idx[v]]++
}
}
} else {
stats := map[string]uint{}
for _, r := range responses {
stats[r.Answer]++
}
for k, v := range stats {
labels = append(labels, k)
values = append(values, v)
}
}
return map[string]interface{}{
"labels": labels,
"datasets": []map[string][]uint{
map[string][]uint{
"values": values,
},
},
}
}
func SurveyWSAdmin(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User)
survey := c.MustGet("survey").(*Survey)
@ -309,6 +367,7 @@ func SurveyWSAdmin(c *gin.Context) {
go func(c chan WSMessage, sid int) {
var v WSMessage
var err error
var surveyTimer *time.Timer
for {
// Reset variable state
v.Corrected = false
@ -326,19 +385,35 @@ func SurveyWSAdmin(c *gin.Context) {
if survey, err := getSurvey(sid); err != nil {
log.Println("Unable to retrieve survey:", err)
} else {
// Skip any existing scheduled timer
if surveyTimer != nil {
if !surveyTimer.Stop() {
<-surveyTimer.C
}
surveyTimer = nil
}
survey.Direct = v.QuestionId
if v.Timer > 0 {
survey.Corrected = false
survey.Update()
go func(corrected bool) {
time.Sleep(time.Duration(OffsetQuestionTimer+v.Timer) * time.Millisecond)
// Save corrected state for the callback
corrected := v.Corrected
with_stats := v.Stats != nil
surveyTimer = time.AfterFunc(time.Duration(OffsetQuestionTimer+v.Timer)*time.Millisecond, func() {
surveyTimer = nil
if corrected {
survey.Corrected = v.Corrected
survey.Update()
survey.WSWriteAll(WSMessage{Action: "new_question", QuestionId: v.QuestionId, Corrected: true, Corrections: getCorrectionString(*v.QuestionId)})
var stats map[string]interface{}
if with_stats {
stats = getResponsesStats(*v.QuestionId)
}
survey.WSWriteAll(WSMessage{Action: "new_question", QuestionId: v.QuestionId, Corrected: true, Stats: stats, Corrections: getCorrectionString(*v.QuestionId)})
} else {
var z int64 = 0
survey.Direct = &z
@ -347,12 +422,20 @@ func SurveyWSAdmin(c *gin.Context) {
survey.WSWriteAll(WSMessage{Action: "pause"})
WSAdminWriteAll(WSMessage{Action: "pause", SurveyId: &survey.Id})
}
}(v.Corrected)
})
v.Corrected = false
v.Stats = nil
} else {
survey.Corrected = v.Corrected
if v.Corrected {
v.Corrections = getCorrectionString(*v.QuestionId)
if v.Stats != nil {
v.Stats = getResponsesStats(*v.QuestionId)
} else {
v.Stats = nil
}
} else {
v.Stats = nil
}
}
_, err = survey.Update()

View file

@ -170,11 +170,13 @@ type GitLabUser struct {
Username string
Name string
State string
Email string
}
type GitLabUserKey struct {
ID int
Key string
ID int
Key string
UsageType string `json:"usage_type"`
}
type GitLabRepository struct {
@ -269,7 +271,7 @@ func GitLab_getUsersRepositories(c context.Context, u *User) ([]*GitLabRepositor
return repositories, err
}
func GitLab_getUserId(c context.Context, u *User) (int, error) {
func GitLab_getUser(c context.Context, u *User) (*GitLabUser, error) {
client := gitlaboauth2Config.Client(c, gitlabToken())
val := url.Values{}
@ -277,26 +279,35 @@ func GitLab_getUserId(c context.Context, u *User) (int, error) {
req, err := http.NewRequest("GET", gitlabBaseURL+fmt.Sprintf("/api/v4/users?%s", val.Encode()), nil)
if err != nil {
return 0, err
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return 0, err
return nil, err
}
if resp.StatusCode != http.StatusOK {
return 0, fmt.Errorf("Bad status code from the API")
return nil, fmt.Errorf("Bad status code from the API")
}
var users []*GitLabUser
err = json.NewDecoder(resp.Body).Decode(&users)
if len(users) == 0 {
return 0, fmt.Errorf("Login not found in GitLab")
return nil, fmt.Errorf("Login not found in GitLab")
}
return users[0].ID, nil
return users[0], nil
}
func GitLab_getUserId(c context.Context, u *User) (int, error) {
user, err := GitLab_getUser(c, u)
if err != nil {
return 0, err
}
return user.ID, nil
}
func GitLab_getUserPGPKeys(c context.Context, u *User) ([]byte, error) {
@ -334,3 +345,33 @@ func GitLab_getUserPGPKeys(c context.Context, u *User) ([]byte, error) {
return b.Bytes(), nil
}
func GitLab_getUserSSHKeys(c context.Context, u *User) ([]*GitLabUserKey, error) {
userid, err := GitLab_getUserId(c, u)
if err != nil {
return nil, err
}
client := gitlaboauth2Config.Client(c, gitlabToken())
req, err := http.NewRequest("GET", gitlabBaseURL+fmt.Sprintf("/api/v4/users/%d/keys", userid), nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
rep, _ := ioutil.ReadAll(resp.Body)
log.Printf("%d %s", resp.StatusCode, rep)
return nil, fmt.Errorf("Bad status code from the API")
}
var keys []*GitLabUserKey
err = json.NewDecoder(resp.Body).Decode(&keys)
return keys, err
}

64
go.mod
View file

@ -1,28 +1,36 @@
module git.nemunai.re/atsebay.t
go 1.18
go 1.21
toolchain go1.22.1
require (
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4
github.com/aws/aws-sdk-go v1.44.135
github.com/coreos/go-oidc/v3 v3.4.0
github.com/ProtonMail/go-crypto v1.0.0
github.com/aws/aws-sdk-go v1.51.9
github.com/coreos/go-oidc/v3 v3.10.0
github.com/drone/drone-go v1.7.1
github.com/gin-gonic/gin v1.8.1
github.com/go-sql-driver/mysql v1.6.0
github.com/jcmturner/gokrb5/v8 v8.4.3
github.com/gin-gonic/gin v1.9.1
github.com/go-sql-driver/mysql v1.8.1
github.com/jcmturner/gokrb5/v8 v8.4.4
github.com/russross/blackfriday/v2 v2.1.0
golang.org/x/oauth2 v0.2.0
nhooyr.io/websocket v1.8.7
golang.org/x/oauth2 v0.18.0
nhooyr.io/websocket v1.8.10
)
require (
github.com/cloudflare/circl v1.1.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/goccy/go-json v0.9.7 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
@ -31,18 +39,22 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.10.3 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

882
go.sum

File diff suppressed because it is too large Load diff

121
gradation.go Normal file
View file

@ -0,0 +1,121 @@
package main
import (
"fmt"
"log"
"math"
"net/http"
"strconv"
"strings"
"github.com/drone/drone-go/drone"
"github.com/gin-gonic/gin"
)
func declareAPIAdminGradationRoutes(router *gin.RouterGroup) {
router.GET("/gradation_repositories", func(c *gin.Context) {
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.RepoList()
if err != nil {
log.Println("Unable to retrieve the repository list:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve the repository list."})
return
}
c.JSON(http.StatusOK, result)
})
router.POST("/gradation_repositories/sync", func(c *gin.Context) {
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.RepoListSync()
if err != nil {
log.Println("Unable to retrieve the repository list:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve the repository list."})
return
}
c.JSON(http.StatusOK, result)
})
}
type TestsWebhook struct {
Login string `json:"login"`
RepositoryId int `json:"repository_id"`
BuildNumber int `json:"build_number"`
UpTo float64 `json:"upto"`
Steps map[string]float64 `json:"steps,omitempty"`
}
func (tw *TestsWebhook) fetchRepoTests(r *Repository) error {
tmp := strings.Split(r.TestsRef, "/")
if len(tmp) < 3 {
return fmt.Errorf("This repository tests reference is not filled properly.")
}
work, err := getWork(r.IdWork)
if err != nil {
return fmt.Errorf("Unable to retrieve the related work: %w", err)
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.Build(tmp[0], tmp[1], tw.BuildNumber)
if err != nil {
return fmt.Errorf("Unable to find the referenced build (%d): %w", tw.BuildNumber, err)
}
if result.Finished > 0 {
return fmt.Errorf("The test phase is not finished")
}
var grade float64
for _, stage := range result.Stages {
for _, step := range stage.Steps {
if g, ok := tw.Steps[fmt.Sprintf("%d", step.Number)]; ok {
log.Printf("Step %q (%d) in status %q", step.Name, step.Number, step.Status)
// Give the point if it succeed
if step.Status == "success" {
grade += g
}
continue
}
if g, ok := tw.Steps[step.Name]; ok {
log.Printf("Step %q (%d) in status %q", step.Name, step.Number, step.Status)
// Give the point if it succeed
if step.Status == "success" {
grade += g
}
continue
}
logs, err := client.Logs(tmp[0], tmp[1], tw.BuildNumber, stage.Number, step.Number)
if err != nil {
log.Printf("Unable to retrieve build logs %s/%s/%d/%d/%d: %s", tmp[0], tmp[1], tw.BuildNumber, stage.Number, step.Number, err.Error())
continue
}
if len(logs) < 2 {
continue
}
line := logs[len(logs)-1]
if strings.HasPrefix(logs[len(logs)-2].Message, "+ echo grade:") && strings.HasPrefix(line.Message, "grade:") {
g, err := strconv.ParseFloat(strings.TrimSpace(strings.TrimPrefix(line.Message, "grade:")), 64)
if err == nil {
grade += g
} else {
log.Println("Unable to parse grade:", err.Error())
}
}
}
}
if tw.UpTo != 0 {
grade = math.Trunc(grade*2000/tw.UpTo) / 100
}
work.AddGrade(WorkGrade{
IdUser: r.IdUser,
IdWork: work.Id,
Grade: grade,
})
return nil
}

View file

@ -1,8 +1,10 @@
package main
import (
"fmt"
"log"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
@ -65,28 +67,44 @@ func declareAPIAuthGradesRoutes(router *gin.RouterGroup) {
})
}
func GetAllGrades() (scores map[int64]map[int64]*float64, err error) {
if rows, errr := DBQuery("SELECT id_user, id_survey, SUM(score)/COUNT(*) FROM student_scores GROUP BY id_user, id_survey"); errr != nil {
func gradeHandler(c *gin.Context) {
work := c.MustGet("work").(*Work)
if gid, err := strconv.Atoi(string(c.Param("gid"))); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad grade identifier."})
return
} else if grade, err := work.GetGrade(int64(gid)); err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Grade not found."})
return
} else {
c.Set("grade", grade)
c.Next()
}
}
func GetAllGrades() (scores map[int64]map[string]*float64, err error) {
if rows, errr := DBQuery("SELECT id_user, kind, id, SUM(score)/COUNT(*) FROM student_scores GROUP BY id_user, kind, id"); errr != nil {
return nil, errr
} else {
defer rows.Close()
scores = map[int64]map[int64]*float64{}
scores = map[int64]map[string]*float64{}
for rows.Next() {
var id_user int64
var id_survey int64
var kind string
var id int64
var score *float64
if err = rows.Scan(&id_user, &id_survey, &score); err != nil {
if err = rows.Scan(&id_user, &kind, &id, &score); err != nil {
return
}
if scores[id_user] == nil {
scores[id_user] = map[int64]*float64{}
scores[id_user] = map[string]*float64{}
}
scores[id_user][id_survey] = score
scores[id_user][fmt.Sprintf("%c.%d", kind[0], id)] = score
}
if err = rows.Err(); err != nil {
return
@ -97,7 +115,7 @@ func GetAllGrades() (scores map[int64]map[int64]*float64, err error) {
}
func (s Survey) GetGrades() (scores map[int64]*float64, err error) {
if rows, errr := DBQuery("SELECT id_question, SUM(score)/COUNT(*) FROM student_scores WHERE id_survey=? GROUP BY id_question", s.Id); errr != nil {
if rows, errr := DBQuery("SELECT id_question, SUM(score)/COUNT(*) FROM student_scores WHERE kind = 'survey' AND id=? GROUP BY id_question", s.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
@ -122,7 +140,7 @@ func (s Survey) GetGrades() (scores map[int64]*float64, err error) {
}
func (s Survey) GetUserGrades(u *User) (scores map[int64]*float64, err error) {
if rows, errr := DBQuery("SELECT id_question, MAX(score) FROM student_scores WHERE id_survey=? AND id_user = ? GROUP BY id_question", s.Id, u.Id); errr != nil {
if rows, errr := DBQuery("SELECT id_question, MAX(score) FROM student_scores WHERE kind = 'survey' AND id=? AND id_user = ? GROUP BY id_question", s.Id, u.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()

38
keys.go
View file

@ -50,6 +50,44 @@ func declareAPIKeysRoutes(router *gin.RouterGroup) {
c.Data(http.StatusOK, "application/pgp-keys", ret)
})
usersRoutes.GET("/allowed_signers", func(c *gin.Context) {
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = c.MustGet("LoggedUser").(*User)
}
user, err := GitLab_getUser(c.Request.Context(), u)
if err != nil {
log.Println("Unable to GitLab_getUser:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve your GitLab user. Please try again in a few moment."})
return
}
keys, err := GitLab_getUserSSHKeys(c.Request.Context(), u)
if err != nil {
log.Println("Unable to GitLab_getUserSSHKeys:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve your keys from GitLab. Please try again in a few moment."})
return
}
var ret string
for _, k := range keys {
if k.UsageType != "auth_and_signing" && k.UsageType != "signing" {
continue
}
if len(user.Email) > 0 {
ret += fmt.Sprintf("%s %s\n", user.Email, k.Key)
} else {
ret += fmt.Sprintf("*@epita.fr %s\n", k.Key)
}
}
c.Data(http.StatusOK, "text/plain", []byte(ret))
})
}
func declareAPIAuthKeysRoutes(router *gin.RouterGroup) {

View file

@ -58,6 +58,7 @@ func main() {
var bind = flag.String("bind", ":8081", "Bind port/socket")
var dsn = flag.String("dsn", DSNGenerator(), "DSN to connect to the MySQL server")
var dummyauth = flag.Bool("dummy-auth", false, "If set, allow any authentication credentials")
flag.StringVar(&mainBanner, "banner-message", mainBanner, "Display a message to connected user, at the top of the screen")
flag.StringVar(&DevProxy, "dev", DevProxy, "Proxify traffic to this host for static assets")
flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL")
flag.UintVar(&currentPromo, "current-promo", currentPromo, "Year of the current promotion")

View file

@ -39,7 +39,7 @@ func declareAPIAuthQuestionsRoutes(router *gin.RouterGroup) {
c.JSON(http.StatusOK, questions)
}
} else {
if (!s.Shown || s.Direct != nil) && !u.IsAdmin {
if s.Direct != nil && !u.IsAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"})
return
}
@ -60,26 +60,10 @@ func declareAPIAuthQuestionsRoutes(router *gin.RouterGroup) {
questionsRoutes := router.Group("/questions/:qid")
questionsRoutes.Use(questionHandler)
questionsRoutes.Use(questionUserAccessHandler)
questionsRoutes.GET("", func(c *gin.Context) {
q := c.MustGet("question").(*Question)
u := c.MustGet("LoggedUser").(*User)
if !u.IsAdmin {
s, err := getSurvey(int(q.IdSurvey))
if err != nil {
log.Println("Unable to getSurvey:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during survey retrieval. Please try again later."})
return
}
if !s.Shown || (s.Direct != nil && *s.Direct != q.Id) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not authorized"})
return
}
}
c.JSON(http.StatusOK, q)
c.JSON(http.StatusOK, c.MustGet("question").(*Question))
})
declareAPIAuthProposalsRoutes(questionsRoutes)
@ -114,6 +98,7 @@ func declareAPIAdminQuestionsRoutes(router *gin.RouterGroup) {
questionsRoutes := router.Group("/questions/:qid")
questionsRoutes.Use(questionHandler)
questionsRoutes.Use(questionUserAccessHandler)
questionsRoutes.PUT("", func(c *gin.Context) {
current := c.MustGet("question").(*Question)
@ -154,6 +139,7 @@ func declareAPIAdminQuestionsRoutes(router *gin.RouterGroup) {
func declareAPIAdminUserQuestionsRoutes(router *gin.RouterGroup) {
questionsRoutes := router.Group("/questions/:qid")
questionsRoutes.Use(questionHandler)
questionsRoutes.Use(questionUserAccessHandler)
questionsRoutes.GET("", func(c *gin.Context) {
question := c.MustGet("question").(*Question)
@ -203,6 +189,38 @@ func questionHandler(c *gin.Context) {
c.Next()
}
func questionUserAccessHandler(c *gin.Context) {
var survey *Survey
if s, ok := c.Get("survey"); ok {
survey = s.(*Survey)
}
u := c.MustGet("LoggedUser").(*User)
question := c.MustGet("question").(*Question)
if survey == nil {
s, err := getSurvey(int(question.IdSurvey))
if err != nil {
log.Println("Unable to getSurvey:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during survey retrieval. Please try again later."})
return
}
survey = s
}
if !u.IsAdmin && (!survey.checkUserAccessToSurvey(u) || (survey.Direct != nil && *survey.Direct != question.Id)) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not authorized"})
return
}
if !u.IsAdmin && survey.StartAvailability.After(time.Now()) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible yet"})
return
}
c.Next()
}
type Question struct {
Id int64 `json:"id"`
IdSurvey int64 `json:"id_survey"`

View file

@ -1,10 +1,7 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"matchPackageNames": ["alpine", "golang.org/x/oauth2", "github.com/aws/aws-sdk-go"],
"automerge": true,
"automergeType": "branch"
}
"extends": [
"local>iac/renovate-config",
"local>iac/renovate-config//automerge-common"
]
}

View file

@ -7,24 +7,30 @@ import (
"fmt"
"log"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/drone/drone-go/drone"
"github.com/gin-gonic/gin"
"golang.org/x/oauth2"
)
var (
droneToken = ""
droneConfig *http.Client
droneEndpoint string
droneToken = ""
droneConfig *http.Client
droneEndpoint string
testsCallbackToken string
)
func init() {
flag.StringVar(&droneToken, "drone-token", droneToken, "Token for Drone Oauth")
flag.StringVar(&droneEndpoint, "drone-endpoint", droneEndpoint, "Drone Endpoint")
flag.StringVar(&testsCallbackToken, "tests-callback-token", testsCallbackToken, "Token of the callback token")
}
func initializeDroneOauth() {
@ -39,6 +45,11 @@ func initializeDroneOauth() {
}
}
type RepositoryAdminPull struct {
Tag *string `json:"tag"`
OptionalSignature bool `json:"sig_optional"`
}
func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
router.GET("/repositories", func(c *gin.Context) {
var u *User
@ -62,7 +73,6 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
if r.IdWork == work.(*Work).Id {
// Is the URL used elsewhere?
repos, _ := getRepositoriesByURI(r.URI)
log.Println(repos)
if len(repos) > 1 {
r.AlreadyUsed = true
}
@ -90,12 +100,39 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
return
}
uri, err := url.Parse(repository.URI)
if err != nil {
tmp := strings.Split(repository.URI, ":")
if len(tmp) == 2 {
uri, err = url.Parse(fmt.Sprintf("ssh://%s/%s", tmp[0], tmp[1]))
} else if len(tmp) == 3 {
uri, err = url.Parse(fmt.Sprintf("%s://%s/%s", tmp[0], tmp[1], tmp[2]))
}
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("invalid repository URL: %s", err.Error())})
return
}
}
if uri.Scheme != "ssh" && uri.Scheme != "git+ssh" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unrecognized URL scheme. You need to provide a SSH repository URL."})
return
}
if strings.Contains(uri.Host, "epita.fr") {
if !strings.HasPrefix(uri.Path, fmt.Sprintf("/%s/", u.Login)) {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "repository URL forbidden"})
return
}
}
var w *Work
if work, ok := c.Get("work"); ok {
w = work.(*Work)
} else if repository.IdWork > 0 {
var err error
w, err = getWork(int(repository.IdWork))
w, err = getWork(repository.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Unable to find the given work identifier."})
return
@ -123,7 +160,6 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
// Is the URL used elsewhere?
repos, _ := getRepositoriesByURI(repo.URI)
log.Println(repos)
if len(repos) > 1 {
repo.AlreadyUsed = true
}
@ -156,8 +192,9 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
}
})
repositoriesRoutes.DELETE("", func(c *gin.Context) {
loggeduser := c.MustGet("LoggedUser").(*User)
repository := c.MustGet("repository").(*Repository)
work, err := getWork(int(repository.IdWork))
work, err := getWork(repository.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
@ -166,7 +203,7 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
now := time.Now()
if !work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Before(now) {
if !loggeduser.IsAdmin && (!work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Before(now)) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "The submission is closed."})
return
}
@ -189,11 +226,10 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
u = loggeduser
}
repo := c.MustGet("repository").(*Repository)
work, err := getWork(int(repo.IdWork))
work, err := getWork(repo.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
now := time.Now()
@ -203,12 +239,12 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
return
}
var tag *string
var rap RepositoryAdminPull
if loggeduser.IsAdmin {
c.ShouldBindJSON(&tag)
c.ShouldBindJSON(&rap)
}
TriggerTagUpdate(c, work, repo, u, tag)
TriggerTagUpdate(c, work, repo, u, rap.Tag, rap.OptionalSignature)
})
repositoriesRoutes.GET("/state", func(c *gin.Context) {
@ -265,6 +301,65 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
c.JSON(http.StatusOK, result)
}
})
repositoriesRoutes.POST("/gradation", func(c *gin.Context) {
loggeduser := c.MustGet("LoggedUser").(*User)
if !loggeduser.IsAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied."})
return
}
var u *User
if user, ok := c.Get("user"); ok {
u = user.(*User)
} else {
u = loggeduser
}
repo := c.MustGet("repository").(*Repository)
work, err := getWork(repo.IdWork)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
TriggerTests(c, work, repo, u)
})
repositoriesRoutes.GET("/gradation_status", func(c *gin.Context) {
loggeduser := c.MustGet("LoggedUser").(*User)
if !loggeduser.IsAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied."})
return
}
repo := c.MustGet("repository").(*Repository)
slug := strings.Split(repo.TestsRef, "/")
if len(slug) < 3 {
return
}
buildn, err := strconv.ParseInt(slug[2], 10, 32)
if err != nil {
return
}
client := drone.NewClient(droneEndpoint, droneConfig)
build, err := client.Build(slug[0], slug[1], int(buildn))
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communicate with Drone"})
return
}
c.JSON(http.StatusOK, build)
})
repositoriesRoutes.GET("/traces", func(c *gin.Context) {
repo := c.MustGet("repository").(*Repository)
c.Redirect(http.StatusFound, fmt.Sprintf("%s/%s", droneEndpoint, repo.TestsRef))
})
}
type GitLabWebhook struct {
@ -309,7 +404,7 @@ func declareCallbacksRoutes(router *gin.RouterGroup) {
return
}
work, err := getWork(int(repo.IdWork))
work, err := getWork(repo.IdWork)
if err != nil {
log.Println("Unable to getWork:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
@ -327,7 +422,7 @@ func declareCallbacksRoutes(router *gin.RouterGroup) {
tmp := strings.SplitN(hook.Ref, "/", 3)
if len(tmp) != 3 {
TriggerTagUpdate(c, work, repo, user, nil)
TriggerTagUpdate(c, work, repo, user, nil, false)
return
}
@ -335,7 +430,7 @@ func declareCallbacksRoutes(router *gin.RouterGroup) {
// Allow to use a secret for another tag
if len(repos) > 1 {
for _, r := range repos {
w, err := getWork(int(r.IdWork))
w, err := getWork(r.IdWork)
if err != nil {
log.Println("Unable to getWork:", err.Error())
continue
@ -355,7 +450,37 @@ func declareCallbacksRoutes(router *gin.RouterGroup) {
}
}
TriggerTagUpdate(c, work, repo, user, &tmp[2])
TriggerTagUpdate(c, work, repo, user, &tmp[2], false)
})
router.POST("/callbacks/tests.json", func(c *gin.Context) {
// Check auth token
if c.GetHeader("X-Authorization") != testsCallbackToken {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": "Authorization token is invalid"})
return
}
// Get form data
hook := TestsWebhook{}
if err := c.ShouldBindJSON(&hook); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
// Retrieve corresponding repository
repo, err := getRepository(hook.RepositoryId)
if err != nil {
log.Printf("Unable to getRepository(%d): %s", hook.RepositoryId, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve repository."})
return
}
err = hook.fetchRepoTests(repo)
if err != nil {
log.Printf("Unable to fetchRepoTests(%d): %s", hook.RepositoryId, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to fetch tests results."})
return
}
})
}
@ -387,11 +512,11 @@ func repositoryHandler(c *gin.Context) {
}
}
func TriggerTagUpdate(c *gin.Context, work *Work, repo *Repository, u *User, tag *string) {
func TriggerTagUpdate(c *gin.Context, work *Work, repo *Repository, u *User, tag *string, sig_optional bool) {
loggeduser := c.MustGet("LoggedUser").(*User)
now := time.Now()
if !loggeduser.IsAdmin && (!work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Add(time.Hour).Before(now)) {
if (loggeduser == nil || !loggeduser.IsAdmin) && (!work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Add(time.Hour).Before(now)) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "The submission is closed."})
return
}
@ -411,21 +536,27 @@ func TriggerTagUpdate(c *gin.Context, work *Work, repo *Repository, u *User, tag
}
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate("srs", "atsebay.t-worker", "", "master", map[string]string{
env := map[string]string{
"REPO_URL": repo.URI,
"REPO_TAG": repo_tag,
"LOGIN": login,
"GROUPS": groups,
"DEST": fmt.Sprintf("%d", work.Id),
})
}
if sig_optional {
env["TAG_SIG_OPTIONAL"] = "1"
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate("teach", "atsebay.t-worker", "", "master", env)
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communication with the extraction service."})
return
}
repo.DroneRef = fmt.Sprintf("%s/%s/%d", "srs", "atsebay.t-worker", result.Number)
repo.DroneRef = fmt.Sprintf("%s/%s/%d", "teach", "atsebay.t-worker", result.Number)
repo.LastCheck = &now
repo.Update()
@ -433,6 +564,116 @@ func TriggerTagUpdate(c *gin.Context, work *Work, repo *Repository, u *User, tag
c.JSON(http.StatusOK, repo)
}
func TriggerTests(c *gin.Context, work *Work, repo *Repository, u *User) {
if work.GradationRepo == nil || len(*work.GradationRepo) == 0 {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "No tests defined for this work."})
return
}
slug := strings.SplitN(*work.GradationRepo, "/", 2)
if len(slug) != 2 {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Graduation repository is invalid."})
return
}
login := u.Login
groups := u.Groups
if u.Id != repo.IdUser {
user, _ := getUser(int(repo.IdUser))
if user != nil {
login = user.Login
groups = user.Groups
}
}
branch := "master"
if len(work.Tag) > 0 {
branch = work.Tag
}
if branch[len(branch)-1] == '-' {
branch += "grades"
}
s, err := s3NewSession()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong."})
return
}
req, _ := s3.New(s).GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(s3_bucket),
Key: aws.String(filepath.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.tar.xz", login))),
})
url, err := req.Presign(SharingTime * 20)
if err != nil {
log.Println("Unable to create presign URL:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong when creating the presigned URL."})
return
}
env := map[string]string{
"SUBMISSION_URL": repo.URI,
"REPO_ID": fmt.Sprintf("%d", repo.Id),
"META_URL": url,
"LOGIN": login,
"GROUPS": groups,
"DEST": fmt.Sprintf("%d", work.Id),
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate(slug[0], slug[1], "", branch, env)
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communication with the gradation service."})
return
}
now := time.Now()
repo.TestsRef = fmt.Sprintf("%s/%d", *work.GradationRepo, result.Number)
repo.LastTests = &now
repo.Update()
repo.Secret = []byte{}
c.JSON(http.StatusOK, repo)
}
func (w *Work) stopTests() error {
repos, err := w.GetRepositories()
if err != nil {
return err
}
client := drone.NewClient(droneEndpoint, droneConfig)
for _, repo := range repos {
slug := strings.Split(repo.TestsRef, "/")
if len(slug) < 3 {
continue
}
buildn, err := strconv.ParseInt(slug[2], 10, 32)
if err != nil {
continue
}
build, err := client.Build(slug[0], slug[1], int(buildn))
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
continue
}
if build.Status == "pending" {
err := client.BuildCancel(slug[0], slug[1], int(buildn))
if err != nil {
log.Println("Unable to cancel the build:", err.Error())
continue
}
}
}
return nil
}
type Repository struct {
Id int64 `json:"id"`
IdUser int64 `json:"id_user"`
@ -441,18 +682,41 @@ type Repository struct {
Secret []byte `json:"secret,omitempty"`
LastCheck *time.Time `json:"last_check"`
DroneRef string `json:"drone_ref,omitempty"`
LastTests *time.Time `json:"last_tests"`
TestsRef string `json:"tests_ref,omitempty"`
AlreadyUsed bool `json:"already_used,omitempty"`
}
func (u *User) GetRepositories() (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE id_user=?", u.Id); errr != nil {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_user=?", u.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef); err != nil {
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef, &repo.LastTests, &repo.TestsRef); err != nil {
return
}
repositories = append(repositories, &repo)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (w *Work) GetRepositories() (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_work=?", w.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef, &repo.LastTests, &repo.TestsRef); err != nil {
return
}
repositories = append(repositories, &repo)
@ -466,14 +730,14 @@ func (u *User) GetRepositories() (repositories []*Repository, err error) {
}
func getRepositoriesByURI(uri string) (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE uri=?", uri); errr != nil {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE uri=?", uri); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef); err != nil {
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef, &repo.LastTests, &repo.TestsRef); err != nil {
return
}
repositories = append(repositories, &repo)
@ -488,13 +752,19 @@ func getRepositoriesByURI(uri string) (repositories []*Repository, err error) {
func getRepository(id int) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE id_repository=?", id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_repository=?", id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef, &r.LastTests, &r.TestsRef)
return
}
func (u *User) getRepository(id int) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE id_repository=? AND id_user=?", id, u.Id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_repository=? AND id_user=?", id, u.Id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef, &r.LastTests, &r.TestsRef)
return
}
func (u *User) getRepositoryByWork(id int64) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_work=? AND id_user=? ORDER BY last_tests DESC LIMIT 1", id, u.Id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef, &r.LastTests, &r.TestsRef)
return
}
@ -505,17 +775,17 @@ func (u *User) NewRepository(w *Work, uri string) (*Repository, error) {
return nil, err
}
if res, err := DBExec("INSERT INTO user_work_repositories (id_user, id_work, uri, secret, droneref) VALUES (?, ?, ?, ?, ?)", u.Id, w.Id, uri, secret, ""); err != nil {
if res, err := DBExec("INSERT INTO user_work_repositories (id_user, id_work, uri, secret, droneref, testsref) VALUES (?, ?, ?, ?, ?, '')", u.Id, w.Id, uri, secret, ""); err != nil {
return nil, err
} else if rid, err := res.LastInsertId(); err != nil {
return nil, err
} else {
return &Repository{rid, u.Id, w.Id, uri, secret, nil, "", false}, nil
return &Repository{rid, u.Id, w.Id, uri, secret, nil, "", nil, "", false}, nil
}
}
func (r *Repository) Update() (*Repository, error) {
if _, err := DBExec("UPDATE user_work_repositories SET id_user = ?, id_work = ?, uri = ?, secret = ?, last_check = ?, droneref = ? WHERE id_repository = ?", r.IdUser, r.IdWork, r.URI, r.Secret, r.LastCheck, r.DroneRef, r.Id); err != nil {
if _, err := DBExec("UPDATE user_work_repositories SET id_user = ?, id_work = ?, uri = ?, secret = ?, last_check = ?, droneref = ?, last_tests = ?, testsref = ? WHERE id_repository = ?", r.IdUser, r.IdWork, r.URI, r.Secret, r.LastCheck, r.DroneRef, r.LastTests, r.TestsRef, r.Id); err != nil {
return nil, err
} else {
return r, err

187
shares.go Normal file
View file

@ -0,0 +1,187 @@
package main
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"fmt"
"log"
"net/http"
"net/url"
"path/filepath"
"github.com/gin-gonic/gin"
)
func declareAPISharesRoutes(router *gin.RouterGroup) {
surveysRoutes := router.Group("/s/surveys/:sid")
surveysRoutes.Use(surveyHandler)
surveysRoutes.Use(sharesAccessHandler)
surveysRoutes.GET("/", func(c *gin.Context) {
share := c.MustGet("survey_share").(*SurveyShared)
share.Count += 1
share.Update()
c.JSON(http.StatusOK, c.MustGet("survey").(*Survey))
})
surveysRoutes.GET("/questions", func(c *gin.Context) {
s := c.MustGet("survey").(*Survey)
if questions, err := s.GetQuestions(); err != nil {
log.Println("Unable to getQuestions:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve questions. Please try again later."})
return
} else {
c.JSON(http.StatusOK, questions)
}
})
questionsRoutes := surveysRoutes.Group("/questions/:qid")
questionsRoutes.Use(questionHandler)
questionsRoutes.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, c.MustGet("question").(*Question))
})
questionsRoutes.GET("/proposals", func(c *gin.Context) {
q := c.MustGet("question").(*Question)
proposals, err := q.GetProposals()
if err != nil {
log.Printf("Unable to GetProposals(qid=%d): %s", q.Id, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during proposals retrieving"})
return
}
c.JSON(http.StatusOK, proposals)
})
questionsRoutes.GET("/responses", func(c *gin.Context) {
q := c.MustGet("question").(*Question)
res, err := q.GetResponses()
if err != nil {
log.Printf("Unable to GetResponses(qid=%d): %s", q.Id, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during responses retrieval."})
return
}
c.JSON(http.StatusOK, res)
})
}
func sharesAccessHandler(c *gin.Context) {
s := c.MustGet("survey").(*Survey)
secret := c.Query("secret")
shares, err := s.getShares()
if err != nil {
log.Printf("Unable to getShares(sid=%d): %s", s.Id, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something went wrong when authenticating the query."})
return
}
for _, share := range shares {
if share.Authenticate(secret) {
c.Set("survey_share", share)
c.Next()
return
}
}
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": "Not authorized"})
}
type SurveyShared struct {
Id int64 `json:"id"`
IdSurvey int64 `json:"id_survey"`
Count int64 `json:"count"`
secret []byte
}
func (s *Survey) Share() (*SurveyShared, error) {
secret := make([]byte, 32)
if _, err := rand.Read(secret); err != nil {
return nil, err
}
if res, err := DBExec("INSERT INTO survey_shared (id_survey, secret) VALUES (?, ?)", s.Id, secret); err != nil {
return nil, err
} else if sid, err := res.LastInsertId(); err != nil {
return nil, err
} else {
return &SurveyShared{sid, s.Id, 0, secret}, nil
}
}
func (sh *SurveyShared) getMAC() []byte {
mac := hmac.New(sha512.New, sh.secret)
mac.Write([]byte(fmt.Sprintf("%d", sh.IdSurvey)))
return mac.Sum(nil)
}
func (sh *SurveyShared) GetURL() (*url.URL, error) {
u, err := url.Parse(oidcRedirectURL)
if err != nil {
return nil, err
}
u.Path = filepath.Join(baseURL, "results")
u.RawQuery = url.Values{
"secret": []string{base64.RawURLEncoding.EncodeToString(sh.getMAC())},
"survey": []string{fmt.Sprintf("%d", sh.IdSurvey)},
}.Encode()
return u, nil
}
func (s *Survey) getShares() (shares []*SurveyShared, err error) {
if rows, errr := DBQuery("SELECT id_share, id_survey, secret, count FROM survey_shared"); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var sh SurveyShared
if err = rows.Scan(&sh.Id, &sh.IdSurvey, &sh.secret, &sh.Count); err != nil {
return
}
shares = append(shares, &sh)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (sh *SurveyShared) Authenticate(secret string) bool {
messageMAC, err := base64.RawURLEncoding.DecodeString(secret)
if err != nil {
return false
}
return hmac.Equal(messageMAC, sh.getMAC())
}
func (sh *SurveyShared) Update() (*SurveyShared, error) {
if _, err := DBExec("UPDATE survey_shared SET id_survey = ?, secret = ?, count = ? WHERE id_share = ?", sh.IdSurvey, sh.secret, sh.Count, sh.Id); err != nil {
return nil, err
} else {
return sh, err
}
}
func (sh SurveyShared) Delete() (int64, error) {
if res, err := DBExec("DELETE FROM survey_shared WHERE id_share = ?", sh.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}

View file

@ -48,15 +48,18 @@ func serveOrReverse(forced_url string) func(c *gin.Context) {
}
func declareStaticRoutes(router *gin.Engine) {
router.GET("/@fs/*_", serveOrReverse(""))
router.GET("/", serveOrReverse(""))
router.GET("/_app/*_", serveOrReverse(""))
router.GET("/auth/", serveOrReverse("/"))
router.GET("/bug-bounty", serveOrReverse("/"))
router.GET("/categories", serveOrReverse("/"))
router.GET("/categories/*_", serveOrReverse("/"))
router.GET("/donnees-personnelles", serveOrReverse("/"))
router.GET("/grades", serveOrReverse("/"))
router.GET("/grades/*_", serveOrReverse("/"))
router.GET("/help", serveOrReverse("/"))
router.GET("/keys", serveOrReverse("/"))
router.GET("/results", serveOrReverse("/"))
router.GET("/surveys", serveOrReverse("/"))
router.GET("/surveys/*_", serveOrReverse("/"))
router.GET("/users", serveOrReverse("/"))
@ -71,7 +74,7 @@ func declareStaticRoutes(router *gin.Engine) {
router.GET("/.svelte-kit/*_", serveOrReverse(""))
router.GET("/node_modules/*_", serveOrReverse(""))
router.GET("/@vite/*_", serveOrReverse(""))
router.GET("/__vite_ping", serveOrReverse(""))
router.GET("/@fs/*_", serveOrReverse(""))
router.GET("/src/*_", serveOrReverse(""))
}
}

View file

@ -17,7 +17,7 @@ import (
"github.com/gin-gonic/gin"
)
const SharingTime = 15 * time.Minute
const SharingTime = 10 * time.Minute
var (
s3_endpoint string

View file

@ -3,6 +3,7 @@ package main
import (
"fmt"
"log"
"math"
"net/http"
"strconv"
"strings"
@ -62,19 +63,14 @@ func declareAPISurveysRoutes(router *gin.RouterGroup) {
return
}
s := c.MustGet("survey").(*Survey)
if (s.Promo == u.Promo && (s.Group == "" || strings.Contains(u.Groups, ","+s.Group+",") && s.Shown)) || u.IsAdmin {
c.JSON(http.StatusOK, s)
} else {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"})
}
c.JSON(http.StatusOK, c.MustGet("survey").(*Survey))
})
}
func declareAPIAuthSurveysRoutes(router *gin.RouterGroup) {
surveysRoutes := router.Group("/surveys/:sid")
surveysRoutes.Use(surveyHandler)
surveysRoutes.Use(surveyUserAccessHandler)
surveysRoutes.GET("/score", func(c *gin.Context) {
var u *User
@ -85,7 +81,34 @@ func declareAPIAuthSurveysRoutes(router *gin.RouterGroup) {
}
s := c.MustGet("survey").(*Survey)
if (s.Promo == u.Promo && s.Shown) || (u != nil && u.IsAdmin) {
if u.IsAdmin {
questions, err := s.GetQuestions()
if err != nil {
log.Println("Unable to getQuestions:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve questions. Please try again later."})
return
}
itemCount := 0
itemCorrected := 0
for _, q := range questions {
res, err := q.GetResponses()
if err != nil {
log.Printf("Unable to GetResponses(qid=%d): %s", q.Id, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during responses retrieval."})
return
}
for _, r := range res {
itemCount += 1
if r.TimeScored != nil && (r.TimeReported == nil || r.TimeScored.After(*r.TimeReported)) {
itemCorrected += 1
}
}
}
c.JSON(http.StatusOK, map[string]int{"count": itemCount, "corrected": itemCorrected})
} else if s.Promo == u.Promo && s.Shown {
score, err := s.GetScore(u)
if err != nil {
log.Printf("Unable to GetScore(uid=%d;sid=%d): %s", u.Id, s.Id, err.Error())
@ -96,7 +119,7 @@ func declareAPIAuthSurveysRoutes(router *gin.RouterGroup) {
if score == nil {
c.JSON(http.StatusOK, map[string]string{"score": "N/A"})
} else {
c.JSON(http.StatusOK, map[string]float64{"score": *score})
c.JSON(http.StatusOK, map[string]float64{"score": math.Round(*score*10) / 10})
}
} else {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"})
@ -123,7 +146,7 @@ func declareAPIAdminSurveysRoutes(router *gin.RouterGroup) {
new.Promo = currentPromo
}
if s, err := NewSurvey(new.Title, new.Promo, new.Group, new.Shown, new.Direct, new.StartAvailability, new.EndAvailability); err != nil {
if s, err := NewSurvey(new.IdCategory, new.Title, new.Promo, new.Group, new.Shown, new.Direct, new.StartAvailability, new.EndAvailability); err != nil {
log.Println("Unable to NewSurvey:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during survey creation: %s", err.Error())})
return
@ -181,6 +204,33 @@ func declareAPIAdminSurveysRoutes(router *gin.RouterGroup) {
}
})
surveysRoutes.GET("shares", func(c *gin.Context) {
survey := c.MustGet("survey").(*Survey)
if sh, err := survey.getShares(); err != nil {
log.Println("Unable to getShares survey:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during survey shares listing: %s", err.Error())})
return
} else {
c.JSON(http.StatusOK, sh)
}
})
surveysRoutes.POST("shares", func(c *gin.Context) {
survey := c.MustGet("survey").(*Survey)
if sh, err := survey.Share(); err != nil {
log.Println("Unable to Share survey:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during survey sharing: %s", err.Error())})
return
} else if url, err := sh.GetURL(); err != nil {
log.Println("Unable to GetURL share:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during survey sharing: %s", err.Error())})
return
} else {
c.JSON(http.StatusOK, url.String())
}
})
declareAPIAdminAsksRoutes(surveysRoutes)
declareAPIAdminDirectRoutes(surveysRoutes)
declareAPIAdminQuestionsRoutes(surveysRoutes)
@ -199,22 +249,25 @@ func surveyHandler(c *gin.Context) {
}
}
func (s *Survey) checkUserAccessToSurvey(u *User) bool {
return u.IsAdmin || (u.Promo == s.Promo && s.Shown && (s.Group == "" || strings.Contains(u.Groups, ","+s.Group+",")))
}
func surveyUserAccessHandler(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User)
w := c.MustGet("survey").(*Survey)
s := c.MustGet("survey").(*Survey)
if u.IsAdmin {
c.Next()
} else if w.Shown && (w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",")) {
c.Next()
} else {
if !s.checkUserAccessToSurvey(u) {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Survey not found."})
return
}
c.Next()
}
type Survey struct {
Id int64 `json:"id"`
IdCategory int64 `json:"id_category"`
Title string `json:"title"`
Promo uint `json:"promo"`
Group string `json:"group"`
@ -226,14 +279,14 @@ type Survey struct {
}
func getSurveys(cnd string, param ...interface{}) (surveys []*Survey, err error) {
if rows, errr := DBQuery("SELECT id_survey, title, promo, grp, shown, direct, corrected, start_availability, end_availability FROM surveys "+cnd, param...); errr != nil {
if rows, errr := DBQuery("SELECT id_survey, id_category, title, promo, grp, shown, direct, corrected, start_availability, end_availability FROM surveys "+cnd, param...); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var s Survey
if err = rows.Scan(&s.Id, &s.Title, &s.Promo, &s.Group, &s.Shown, &s.Direct, &s.Corrected, &s.StartAvailability, &s.EndAvailability); err != nil {
if err = rows.Scan(&s.Id, &s.IdCategory, &s.Title, &s.Promo, &s.Group, &s.Shown, &s.Direct, &s.Corrected, &s.StartAvailability, &s.EndAvailability); err != nil {
return
}
surveys = append(surveys, &s)
@ -255,7 +308,7 @@ func getSurvey(id int) (s *Survey, err error) {
}
s = new(Survey)
err = DBQueryRow("SELECT id_survey, title, promo, grp, shown, direct, corrected, start_availability, end_availability FROM surveys WHERE id_survey=?", id).Scan(&s.Id, &s.Title, &s.Promo, &s.Group, &s.Shown, &s.Direct, &s.Corrected, &s.StartAvailability, &s.EndAvailability)
err = DBQueryRow("SELECT id_survey, id_category, title, promo, grp, shown, direct, corrected, start_availability, end_availability FROM surveys WHERE id_survey=?", id).Scan(&s.Id, &s.IdCategory, &s.Title, &s.Promo, &s.Group, &s.Shown, &s.Direct, &s.Corrected, &s.StartAvailability, &s.EndAvailability)
_surveys_cache_mutex.Lock()
_surveys_cache[int64(id)] = s
@ -263,13 +316,13 @@ func getSurvey(id int) (s *Survey, err error) {
return
}
func NewSurvey(title string, promo uint, group string, shown bool, direct *int64, startAvailability time.Time, endAvailability time.Time) (*Survey, error) {
if res, err := DBExec("INSERT INTO surveys (title, promo, grp, shown, direct, start_availability, end_availability) VALUES (?, ?, ?, ?, ?, ?, ?)", title, promo, group, shown, direct, startAvailability, endAvailability); err != nil {
func NewSurvey(id_category int64, title string, promo uint, group string, shown bool, direct *int64, startAvailability time.Time, endAvailability time.Time) (*Survey, error) {
if res, err := DBExec("INSERT INTO surveys (id_category, title, promo, grp, shown, direct, start_availability, end_availability) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", id_category, title, promo, group, shown, direct, startAvailability, endAvailability); err != nil {
return nil, err
} else if sid, err := res.LastInsertId(); err != nil {
return nil, err
} else {
return &Survey{sid, title, promo, group, shown, direct, false, startAvailability, endAvailability}, nil
return &Survey{sid, id_category, title, promo, group, shown, direct, false, startAvailability, endAvailability}, nil
}
}
@ -289,7 +342,7 @@ func (s Survey) GetScore(u *User) (score *float64, err error) {
if ok {
score = v
} else {
err = DBQueryRow("SELECT SUM(score)/COUNT(*) FROM student_scores WHERE id_survey=? AND id_user=?", s.Id, u.Id).Scan(&score)
err = DBQueryRow("SELECT SUM(score)/COUNT(*) FROM student_scores WHERE kind = 'survey' AND id=? AND id_user=?", s.Id, u.Id).Scan(&score)
if score != nil {
*score = *score / 5.0
}
@ -302,7 +355,7 @@ func (s Survey) GetScore(u *User) (score *float64, err error) {
}
func (s Survey) GetScores() (scores map[int64]*float64, err error) {
if rows, errr := DBQuery("SELECT id_user, SUM(score)/COUNT(*) FROM student_scores WHERE id_survey=? GROUP BY id_user", s.Id); errr != nil {
if rows, errr := DBQuery("SELECT id_user, SUM(score)/COUNT(*) FROM student_scores WHERE kind = 'survey' AND id_survey=? GROUP BY id_user", s.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
@ -327,7 +380,7 @@ func (s Survey) GetScores() (scores map[int64]*float64, err error) {
}
func (s *Survey) Update() (*Survey, error) {
if _, err := DBExec("UPDATE surveys SET title = ?, promo = ?, grp = ?, shown = ?, direct = ?, corrected = ?, start_availability = ?, end_availability = ? WHERE id_survey = ?", s.Title, s.Promo, s.Group, s.Shown, s.Direct, s.Corrected, s.StartAvailability, s.EndAvailability, s.Id); err != nil {
if _, err := DBExec("UPDATE surveys SET id_category = ?, title = ?, promo = ?, grp = ?, shown = ?, direct = ?, corrected = ?, start_availability = ?, end_availability = ? WHERE id_survey = ?", s.IdCategory, s.Title, s.Promo, s.Group, s.Shown, s.Direct, s.Corrected, s.StartAvailability, s.EndAvailability, s.Id); err != nil {
return nil, err
} else {
_surveys_cache_mutex.Lock()
@ -338,6 +391,7 @@ func (s *Survey) Update() (*Survey, error) {
}
func (s Survey) Delete() (int64, error) {
DBExec("DELETE FROM survey_shared WHERE id_survey = ?", s.Id)
if res, err := DBExec("DELETE FROM surveys WHERE id_survey = ?", s.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {

4302
ui/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -11,25 +11,26 @@
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
},
"devDependencies": {
"@sveltejs/adapter-static": "^1.0.0-next.29",
"@sveltejs/kit": "^1.0.0-next.324",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.0.0",
"@sveltejs/adapter-static": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^4.0.0",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",
"svelte": "^3.48.0",
"svelte-check": "^2.7.0",
"svelte-preprocess": "^4.10.6",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.35.0",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.1.2",
"svelte": "^4.0.0",
"svelte-check": "^3.4.3",
"svelte-preprocess": "^5.0.3",
"tslib": "^2.4.0",
"typescript": "^4.6.4"
"typescript": "^5.0.0",
"vite": "^5.0.0"
},
"type": "module",
"dependencies": {
"dayjs": "^1.11.5",
"svelte-frappe-charts": "^1.9.1",
"vite": "^3.0.4"
"svelte-frappe-charts": "^1.9.1"
}
}

View file

@ -1,13 +0,0 @@
<script>
export let survey;
let className = '';
export { className as class };
</script>
{#if survey.direct != null}<span class="badge bg-danger {className}">Direct</span>
{:else if survey.startAvailability() > Date.now()}<span class="badge bg-info {className}">Prévu</span>
{:else if survey.endAvailability() > Date.now()}<span class="badge bg-warning {className}">En cours</span>
{:else if !survey.__start_availability}<span class="badge bg-dark {className}">Nouveau</span>
{:else if !survey.corrected}<span class="badge bg-primary text-light {className}">Terminé</span>
{:else}<span class="badge bg-success {className}">Corrigé</span>
{/if}

View file

@ -1,6 +0,0 @@
export async function handle({ event, resolve }) {
const response = await resolve(event, {
ssr: false,
});
return response;
}

60
ui/src/lib/categories.js Normal file
View file

@ -0,0 +1,60 @@
export async function getCategories() {
let url = '/api/categories';
const res = await fetch(url, {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
return (await res.json()).map((r) => new Category(r));
} else {
throw new Error((await res.json()).errmsg);
}
}
export class Category {
constructor(res) {
if (res) {
this.update(res);
}
}
update({ id, label, promo, expand }) {
this.id = id;
this.label = label;
this.promo = promo;
this.expand = expand;
}
async save() {
const res = await fetch(this.id?`api/categories/${this.id}`:'api/categories', {
method: this.id?'PUT':'POST',
headers: {'Accept': 'application/json'},
body: JSON.stringify(this),
});
if (res.status == 200) {
const data = await res.json()
this.update(data);
return data;
} else {
throw new Error((await res.json()).errmsg);
}
}
async delete() {
const res = await fetch(`api/categories/${this.id}`, {
method: 'DELETE',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return true;
} else {
throw new Error((await res.json()).errmsg);
}
}
}
export async function getCategory(cid) {
const res = await fetch(`api/categories/${cid}`, {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
return new Category(await res.json());
} else {
throw new Error((await res.json()).errmsg);
}
}

View file

@ -20,7 +20,7 @@
{:else if state.status == "success"}
<i class="bi bi-check-circle-fill text-success mx-1" title="La récupération s'est bien passée"></i>
{:else if state.status == "failure" || state.status == "killed"}
<i class="bi bi-exclamation-circle-fill text-danger mx-1" title="La récupération ne s'est pas bien passée" style="cursor: pointer" on:click={() => dispatch('show_logs')}></i>
<i class="bi bi-exclamation-circle-fill text-danger mx-1" title="La récupération ne s'est pas bien passée" style="cursor: pointer" on:click={() => dispatch('show_logs')} on:keypress={() => dispatch('show_logs')}></i>
{:else}
{state.status}
{/if}

View file

@ -0,0 +1,92 @@
<script>
import { createEventDispatcher } from 'svelte';
import { goto } from '$app/navigation';
import DateTimeInput from './DateTimeInput.svelte';
import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher();
export let category = null;
function saveCategory() {
category.save().then((response) => {
dispatch('saved', response);
}, (error) => {
ToastsStore.addErrorToast({
msg: error,
});
})
}
function deleteCategory() {
category.delete().then((response) => {
goto(`categories`);
}, (error) => {
ToastsStore.addErrorToast({
msg: error,
});
})
}
function duplicateCategory() {
category.duplicate().then((response) => {
goto(`categories/${response.id}`);
}).catch((error) => {
ToastsStore.addErrorToast({
msg: error,
});
})
}
</script>
<form on:submit|preventDefault={saveCategory}>
{#if category.id}
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="cid" class="col-form-label col-form-label-sm">Identifiant de la catégorie</label>
</div>
<div class="col-sm-8">
<input type="text" class="form-control-plaintext form-control-sm" id="cid" value={category.id}>
</div>
</div>
{/if}
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="title" class="col-form-label col-form-label-sm">Titre de la catégorie</label>
</div>
<div class="col-sm-8">
<input type="text" class="form-control form-control-sm" id="title" bind:value={category.label}>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="promo" class="col-form-label col-form-label-sm">Promo</label>
</div>
<div class="col-sm-8 col-md-4 col-lg-2">
<input type="number" step="1" min="0" max="2068" class="form-control form-control-sm" id="promo" bind:value={category.promo}>
</div>
</div>
<div class="row row-cols-3 mx-1 my-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="expand" bind:checked={category.expand}>
<label class="form-check-label" for="expand">
Étendre par défaut
</label>
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-primary">Enregistrer</button>
{#if category.id}
<button type="button" class="btn btn-danger" on:click={deleteCategory}>Supprimer</button>
{/if}
</div>
</div>
</form>

View file

@ -7,7 +7,7 @@
export let question = null;
function refreshProposals() {
let req = question.getProposals();
let req = question.getProposals(secret);
req.then((proposals) => {
const proposal_idx = { };
@ -17,7 +17,7 @@
proposal_idx[proposal.id] = new String(data.labels.length - 1);
}
req_responses = question.getResponses();
req_responses = question.getResponses(secret);
req_responses.then((responses) => {
for (const res of responses) {
const rsplt = res.value.split(',');
@ -32,6 +32,7 @@
}
let req_proposals = null;
export let proposals = null;
export let secret = null;
let req_responses = null;
let mean = null;
@ -46,7 +47,7 @@
if (!proposals) {
if (question.kind && (question.kind == "int" || question.kind.startsWith("list"))) {
req_responses = question.getResponses();
req_responses = question.getResponses(secret);
req_responses.then((responses) => {
const values = [];
const proposal_idx = { };

View file

@ -1,5 +1,5 @@
<script>
import { CorrectionTemplate } from '../lib/correctionTemplates';
import { CorrectionTemplate } from '$lib/correctionTemplates';
let className = '';
export { className as class };

View file

@ -1,6 +1,6 @@
<script>
import { user } from '../stores/user';
import { autoCorrection } from '../lib/correctionTemplates';
import { user } from '$lib/stores/user';
import { autoCorrection } from '$lib/correctionTemplates';
export let cts = null;
export let rid = 0;
@ -38,14 +38,37 @@
for (const t of templates) {
if (my_tpls[t.id] === undefined && cts[t.id.toString()]) {
my_tpls[t.id] = cts[t.id.toString()][response.id_user] !== undefined;
// Hack to autocorrect only if this has already been checked previously
if (autoCorrectionInProgress && cts[t.id.toString()][response.id_user] !== undefined) {
autoCorrectionInProgress = false;
}
}
}
}
}
let element = null;
let scrollY = 0;
let autoCorrectionInProgress = true;
$: {
if (element && scrollY > element.offsetParent.offsetTop - 500 && !my_correction) {
let tmp = false;
[tmp, autoCorrectionInProgress] = [autoCorrectionInProgress, true];
if (!tmp) {
autoCorrection(response.id_user, my_tpls).then((r) => {
my_correction = r;
autoCorrectionInProgress = false;
})
}
}
}
</script>
<svelte:window bind:scrollY={scrollY}/>
<form
class="row"
bind:this={element}
on:submit|preventDefault={submitCorrection}
>
<div class="col-auto">
@ -69,6 +92,7 @@
>
<label
class="form-check-label"
class:fw-bold={template.regexp && (template.regexp[0] == "!" ? !response.value.match(new RegExp(template.regexp.substring(1))) : response.value.match(new RegExp(template.regexp)))}
for="r{response.id}t{template.id}"
>
{template.label}

View file

@ -4,8 +4,8 @@
import QuestionProposals from './QuestionProposals.svelte';
import ResponseCorrected from './ResponseCorrected.svelte';
import CorrectionResponseFooter from './CorrectionResponseFooter.svelte';
import { autoCorrection } from '../lib/correctionTemplates';
import { getUser } from '../lib/users';
import { autoCorrection } from '$lib/correctionTemplates';
import { getUser } from '$lib/users';
export let cts = null;
export let filter = "";
@ -35,20 +35,46 @@
filteredResponses = responses.filter((r) => (notCorrected || r.time_scored <= r.time_reported || !r.time_scored) && (!filter || ((filter[0] == '!' && !r.value.match(filter.substring(1))) || r.value.match(filter))));
}
function escapeTags(htmlStr) {
return htmlStr.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function hilightText(input, templates) {
for (const { regexp } of templates) {
if (regexp) {
input = input.replace(new RegExp(regexp, 'g'), '<ins class="fw-bold">$&</ins>')
}
}
return input;
}
export async function applyCorrections() {
for (const r of filteredResponses) {
const my_correction = { };
let has_no_lost_answer = false;
let completed_correction = false;
for (const tpl of templates) {
if (tpl.score >= 0) has_no_lost_answer = true;
if (!tpl.regexp && tpl.label) continue;
if (tpl.regexp && (tpl.regexp[0] == '!' && !r.value.match(tpl.regexp.substring(1))) || r.value.match(tpl.regexp)) {
my_correction[tpl.id] = true;
completed_correction = true;
} else {
my_correction[tpl.id] = false;
}
}
// If no valid correction template AND valid answer is defined,
// don't consider the absence of match as valid answer.
if (!completed_correction && has_no_lost_answer) continue;
const auto = await autoCorrection(r.id_user, my_correction);
r.score = auto.score;
r.score_explaination = auto.score_explaination;
@ -88,7 +114,7 @@
class="card-text"
style="white-space: pre-line"
>
{response.value}
{@html hilightText(escapeTags(response.value), templates)}
</p>
{/if}
<ResponseCorrected

View file

@ -47,7 +47,7 @@
</span>
<div>
{#each res[rep] as user}
<a href="users/{user}" target="_blank" class="badge bg-dark rounded-pill">
<a href="users/{user}" target="_blank" rel="noreferrer" class="badge bg-dark rounded-pill">
{#if users && users[user]}
{users[user].login}
{:else}
@ -64,7 +64,7 @@
<span>
{rep}
</span>
<a href="users/{res[rep]}" target="_blank" class="badge bg-dark rounded-pill">
<a href="users/{res[rep]}" target="_blank" rel="noreferrer" class="badge bg-dark rounded-pill">
{#if users && users[res[rep]]}
{users[res[rep]].login}
{:else}

View file

@ -5,7 +5,7 @@
import QuestionHeader from './QuestionHeader.svelte';
import QuestionProposals from './QuestionProposals.svelte';
import ResponseCorrected from './ResponseCorrected.svelte';
import { user } from '../stores/user';
import { user } from '$lib/stores/user';
const dispatch = createEventDispatcher();

View file

@ -1,7 +1,7 @@
<script>
import { createEventDispatcher } from 'svelte';
import { user } from '../stores/user';
import { user } from '$lib/stores/user';
const dispatch = createEventDispatcher();

View file

@ -1,7 +1,7 @@
<script>
import { createEventDispatcher } from 'svelte';
import { QuestionProposal } from '../lib/questions';
import { QuestionProposal } from '$lib/questions';
export let edit = false;
export let proposals = [];

View file

@ -1,6 +1,6 @@
<script>
import { user } from '../stores/user';
import { ToastsStore } from '../stores/toasts';
import { user } from '$lib/stores/user';
import { ToastsStore } from '$lib/stores/toasts';
export let response = null;
export let survey = null;

View file

@ -0,0 +1,14 @@
<script>
export let score = 0;
</script>
<span
class="badge"
class:bg-success={score >= 18}
class:bg-info={score < 18 && score >= 15}
class:bg-warning={score < 15 && score >= 9}
class:bg-danger={score < 9}
class:bg-dark={score == "N/A"}
>
{score}
</span>

View file

@ -1,8 +1,10 @@
<script>
import { getSurveys } from '../lib/surveys';
import { getUsers, getGrades, getPromos } from '../lib/users';
import { getCategories } from '$lib/categories';
import { getSurveys } from '$lib/surveys';
import { getUsers, getGrades, getPromos } from '$lib/users';
export let promo = null;
export let category = null;
</script>
{#await getPromos() then promos}
@ -15,12 +17,24 @@
</select>
</div>
{/await}
{#await getCategories() then categories}
<div class="float-end me-2">
<select class="form-select" bind:value={category}>
<option value={null}>toutes</option>
{#each categories as categ (categ.id)}
{#if !promo || categ.promo == promo}
<option value={categ.id}>{categ.label}</option>
{/if}
{/each}
</select>
</div>
{/await}
<h2>
Étudiants {#if promo !== null}{promo}{/if}
<small class="text-muted">Notes</small>
</h2>
{#await getSurveys()}
{#await getSurveys(true)}
<div class="d-flex justify-content-center">
<div class="spinner-border me-2" role="status"></div>
Chargement des questionnaires corrigés&hellip;
@ -38,9 +52,15 @@
<tr>
<th>ID</th>
<th>Login</th>
{#each surveys as survey (survey.id)}
{#if survey.corrected && (promo === null || survey.promo == promo)}
<th><a href="surveys/{survey.id}" style="text-decoration: none">{survey.title}</a></th>
{#each surveys as survey}
{#if survey.corrected && (!promo || survey.promo == promo) && (!category || survey.id_category == category)}
<th>
{#if survey.kind == "survey"}
<a href="surveys/{survey.id}" style="text-decoration: none">{survey.title}</a>
{:else}
<a href="works/{survey.id}" style="text-decoration: none">{survey.title}</a>
{/if}
</th>
{/if}
{/each}
</tr>
@ -57,13 +77,15 @@
</tr>
{:then users}
{#each users as user (user.id)}
{#if promo === null || user.promo === promo}
{#if !promo || user.promo == promo}
<tr>
<td><a href="users/{user.id}" style="text-decoration: none">{user.id}</a></td>
<td><a href="users/{user.login}" style="text-decoration: none">{user.login}</a></td>
{#each surveys as survey (survey.id)}
{#if survey.corrected && (promo === null || survey.promo == promo)}
<td>{grades[user.id] && grades[user.id][survey.id]?grades[user.id][survey.id]:""}</td>
{#each surveys as survey}
{#if survey.corrected && (!promo || survey.promo == promo) && (!category || survey.id_category == category)}
<td>
{grades[user.id] && grades[user.id][survey.kind + "." + survey.id]?grades[user.id][survey.kind + "." + survey.id]:""}
</td>
{/if}
{/each}
</tr>

View file

@ -1,11 +1,8 @@
<script>
import { createEventDispatcher } from 'svelte';
import DateFormat from '../components/DateFormat.svelte';
import { getUserRendu } from '../lib/works';
let className = '';
export { className as class };
import DateFormat from '$lib/components/DateFormat.svelte';
import { getUserRendu } from '$lib/works';
export let work = null;
export let user = null;

View file

@ -2,9 +2,10 @@
import { createEventDispatcher } from 'svelte';
import { goto } from '$app/navigation';
import { getQuestions } from '../lib/questions';
import { getCategories } from '$lib/categories';
import { getQuestions } from '$lib/questions';
import DateTimeInput from './DateTimeInput.svelte';
import { ToastsStore } from '../stores/toasts';
import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher();
export let survey = null;
@ -33,10 +34,14 @@
})
}
let duplicateInProgress = false;
function duplicateSurvey() {
duplicateInProgress = true;
survey.duplicate().then((response) => {
duplicateInProgress = false;
goto(`surveys/${response.id}`);
}).catch((error) => {
duplicateInProgress = false;
ToastsStore.addErrorToast({
msg: error,
});
@ -67,6 +72,21 @@
</div>
</div>
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="category" class="col-form-label col-form-label-sm">Catégorie</label>
</div>
<div class="col-sm-8 col-md-4 col-lg-2">
{#await getCategories() then categories}
<select id="category" class="form-select form-select-sm" bind:value={survey.id_category}>
{#each categories as category (category.id)}
<option value={category.id}>{category.label} {category.promo}</option>
{/each}
</select>
{/await}
</div>
</div>
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="promo" class="col-form-label col-form-label-sm">Promo</label>
@ -108,7 +128,7 @@
<div class="col-sm-3 text-sm-end">
<label for="start_availability" class="col-form-label col-form-label-sm">Date de début</label>
</div>
<div class="col-sm-8">
<div class="col-sm-8 col-md-5 col-lg-3">
<DateTimeInput class="form-control form-control-sm" id="start_availability" bind:date={survey.start_availability} />
</div>
</div>
@ -117,7 +137,7 @@
<div class="col-sm-3 text-sm-end">
<label for="end_availability" class="col-form-label col-form-label-sm">Date de fin</label>
</div>
<div class="col-8">
<div class="col-sm-8 col-md-5 col-lg-3">
<DateTimeInput class="form-control form-control-sm" id="end_availability" bind:date={survey.end_availability} />
</div>
</div>
@ -141,14 +161,19 @@
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-primary">Enregistrer</button>
{#if survey.id}
<button type="button" class="btn btn-danger" on:click={deleteSurvey} disabled={deleteInProgress}>
{#if survey.id || duplicateInProgress}
<button type="button" class="btn btn-danger" on:click={deleteSurvey} disabled={deleteInProgress || duplicateInProgress}>
{#if deleteInProgress}
<div class="spinner-border spinner-border-sm text-light me-1" role="status"></div>
{/if}
Supprimer
</button>
<button type="button" class="btn btn-secondary" on:click={duplicateSurvey}>Dupliquer avec ces nouveaux paramètres</button>
<button type="button" class="btn btn-secondary" on:click={duplicateSurvey} disabled={duplicateInProgress}>
{#if duplicateInProgress}
<div class="spinner-border spinner-border-sm text-dark me-1" role="status"></div>
{/if}
Dupliquer avec ces nouveaux paramètres
</button>
{/if}
</div>
</div>

View file

@ -0,0 +1,13 @@
<script>
export let survey;
let className = '';
export { className as class };
</script>
{#if survey.direct != null}<span class="badge bg-danger {className}">Direct</span>
{:else if survey.startAvailability() > Date.now()}<span class="badge bg-info {className}" title="Le questionnaire ouvre le {survey.startAvailability()}">Prévu</span>
{:else if survey.endAvailability() > Date.now()}<span class="badge bg-warning {className}" title="Le questionnaire se termine le {survey.endAvailability()}">En cours</span>
{:else if !survey.__start_availability}<span class="badge bg-dark {className}">Nouveau</span>
{:else if !survey.corrected}<span class="badge bg-primary text-light {className}" title="Le questionnaire s'est terminé le {survey.endAvailability()}">Terminé</span>
{:else}<span class="badge bg-success {className}" title="Le questionnaire s'est terminé le {survey.endAvailability()} et est désormais corrigé">Corrigé</span>
{/if}

View file

@ -1,12 +1,14 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { user } from '../stores/user';
import DateFormat from '../components/DateFormat.svelte';
import SurveyBadge from '../components/SurveyBadge.svelte';
import SubmissionStatus from '../components/SubmissionStatus.svelte';
import { getSurveys } from '../lib/surveys';
import { getScore } from '../lib/users';
import { user } from '$lib/stores/user';
import DateFormat from '$lib/components/DateFormat.svelte';
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
import { getCategories } from '$lib/categories';
import { getSurveys } from '$lib/surveys';
import { getScore } from '$lib/users';
export let allworks = false;
@ -21,6 +23,13 @@
}
});
let categories = {};
getCategories().then((cs) => {
for (const c of cs) {
categories[c.id] = c;
}
});
function gotoSurvey(survey) {
if (survey.kind === "w") {
goto(`works/${survey.id}`);
@ -40,7 +49,11 @@
<th>Intitulé</th>
<th>Date</th>
{#if $user}
<th>Score</th>
{#if $user.is_admin}
<th>À corriger</th>
{:else}
<th>Score</th>
{/if}
{/if}
</tr>
</thead>
@ -56,13 +69,39 @@
{#each surveys as survey, sid (survey.kind + survey.id)}
{#if (survey.shown || survey.direct == null || ($user && $user.is_admin)) && (!$user || (!$user.was_admin || $user.promo == survey.promo) || $user.is_admin)}
{#if $user && $user.is_admin && (sid == 0 || surveys[sid-1].promo != survey.promo)}
<tr class="bg-info text-light">
<tr class="bg-warning text-light">
<th colspan="5" class="fw-bold">
{survey.promo}
</th>
</tr>
{/if}
<tr on:click={e => gotoSurvey(survey)}>
{#if $user && (sid == 0 || surveys[sid-1].id_category != survey.id_category) && categories[survey.id_category]}
<tr class="bg-primary text-light">
<th
colspan="5"
class="fw-bold"
on:click={() => categories[survey.id_category].expand = !categories[survey.id_category].expand}
on:keypress={() => categories[survey.id_category].expand = !categories[survey.id_category].expand}
>
{#if categories[survey.id_category].expand}
<i class="bi bi-chevron-down"></i>
{:else}
<i class="bi bi-chevron-right"></i>
{/if}
{categories[survey.id_category].label}
{#if $user && $user.is_admin}
<a href="categories/{survey.id_category}" class="float-end btn btn-sm btn-light" style="margin: -6px;">
<i class="bi bi-pencil"></i>
</a>
{/if}
</th>
</tr>
{/if}
{#if categories[survey.id_category] && categories[survey.id_category].expand}
<tr
on:click={e => gotoSurvey(survey)}
on:keypress={e => gotoSurvey(survey)}
>
<td>
{#if !survey.shown}<i class="bi bi-eye-slash-fill" title="Ce questionnaire n'est pas affiché aux étudiants"></i>{/if}
{survey.title}
@ -84,14 +123,30 @@
</td>
{/if}
{#if $user}
{#if !survey.corrected}
{#if !survey.corrected && !$user.is_admin}
<td>N/A</td>
{:else}
<td>
{#await getScore(survey)}
<div class="spinner-border spinner-border-sm" role="status"></div>
{:then score}
{score.score}
{#if score.count !== undefined}
<span
class:fw-bolder={score.count-score.corrected > 0}
class:badge={survey.corrected}
class:bg-danger={survey.corrected && score.count-score.corrected > 0}
class:bg-dark={survey.corrected && score.count-score.corrected <= 0}
title="{score.count-score.corrected}/{score.count}"
>
{#if score.count == 0 || score.corrected == 0 || survey.corrected}
{score.count-score.corrected}
{:else}
{Math.trunc((1-score.corrected/score.count)*100)}&nbsp;%
{/if}
</span>
{:else}
<ScoreBadge score={score.score} />
{/if}
{:catch error}
<i class="bi text-warning bi-exclamation-triangle-fill" title={error}></i>
{/await}
@ -99,6 +154,7 @@
{/if}
{/if}
</tr>
{/if}
{/if}
{/each}
</tbody>

View file

@ -1,8 +1,8 @@
<script>
import { user } from '../stores/user';
import { ToastsStore } from '../stores/toasts';
import QuestionForm from '../components/QuestionForm.svelte';
import { Question } from '../lib/questions';
import { user } from '$lib/stores/user';
import { ToastsStore } from '$lib/stores/toasts';
import QuestionForm from '$lib/components/QuestionForm.svelte';
import { Question } from '$lib/questions';
export let survey = null;
export let id_user = null;
@ -25,7 +25,7 @@
survey.submitAnswers(res, id_user).then((response) => {
submitInProgress = false;
ToastsStore.addToast({
msg: "Vos réponses ont bien étés sauvegardées.",
msg: "Vos réponses ont bien été sauvegardées.",
color: "success",
title: "Questionnaire",
});

View file

@ -1,5 +1,5 @@
<script>
import { ToastsStore } from '../stores/toasts';
import { ToastsStore } from '$lib/stores/toasts';
</script>
<div class="toast-container position-fixed top-0 end-0 p-3">

View file

@ -0,0 +1,15 @@
<script>
export let status = null;
</script>
{#if status}
<span
class="badge"
class:bg-success={status == "success"}
class:bg-danger={status == "failure" || status == "killed"}
class:bg-warning={status == "pending" || status == "running"}
class:bg-dark={status != "success" && status != "failure" && status != "killed" && status != "pending" && status != "running"}
>
{status}
</span>
{/if}

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { getKeys, getKey, Key } from '../lib/key';
import { getKeys, getKey, Key } from '$lib/key';
export let student = null;
</script>

View file

@ -1,8 +1,8 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { getSurveys } from '../lib/surveys';
import { getUser, getUserGrade, getUserScore } from '../lib/users';
import { getSurveys } from '$lib/surveys';
import { getUser, getUserGrade, getUserScore } from '$lib/users';
export let student = null;
export let allPromos = false;

View file

@ -1,6 +1,6 @@
<script>
import { user } from '../stores/user';
import DateFormat from '../components/DateFormat.svelte';
import { user } from '$lib/stores/user';
import DateFormat from '$lib/components/DateFormat.svelte';
let className = '';
export { className as class };

View file

@ -2,8 +2,10 @@
import { createEventDispatcher } from 'svelte';
import { goto } from '$app/navigation';
import { getCategories } from '$lib/categories';
import { getGradationRepositories, syncGradationRepositories } from '$lib/gradation';
import DateTimeInput from './DateTimeInput.svelte';
import { ToastsStore } from '../stores/toasts';
import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher();
export let work = null;
@ -38,6 +40,7 @@
})
}
let grepositoriesP = getGradationRepositories();
</script>
<form on:submit|preventDefault={saveWork}>
@ -62,6 +65,21 @@
</div>
</div>
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="category" class="col-form-label col-form-label-sm">Catégorie</label>
</div>
<div class="col-sm-8 col-md-4 col-lg-2">
{#await getCategories() then categories}
<select id="category" class="form-select form-select-sm" bind:value={work.id_category}>
{#each categories as category (category.id)}
<option value={category.id}>{category.label} {category.promo}</option>
{/each}
</select>
{/await}
</div>
</div>
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="promo" class="col-form-label col-form-label-sm">Promo</label>
@ -93,16 +111,46 @@
<div class="col-sm-3 text-sm-end">
<label for="submissionurl" class="col-form-label col-form-label-sm">URL validation la soumission</label>
</div>
<div class="col-sm-8 col-md-4 col-lg-2">
<div class="col-sm-10 col-md-8 col-lg-4">
<input class="form-control form-control-sm" id="submissionurl" bind:value={work.submission_url}>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="gradationrepo" class="col-form-label col-form-label-sm">Dépôt des tests automatiques</label>
</div>
<div class="col-sm-10 col-md-8 col-lg-4 d-flex align-items-center">
{#await grepositoriesP}
<div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span>
</div>
{:then grepositories}
<div class="input-group">
<select class="form-select form-select-sm" id="gradationrepo" bind:value={work.gradation_repo}>
<option value={null}>-</option>
{#each grepositories as r}
<option value={r.slug}>{r.slug}</option>
{/each}
</select>
<button
type="button"
class="btn btn-light btn-sm"
title="Synchroniser les dépôts"
on:click={() => grepositoriesP = syncGradationRepositories()}
>
<i class="bi bi-arrow-repeat"></i>
</button>
</div>
{/await}
</div>
</div>
<div class="row">
<div class="col-sm-3 text-sm-end">
<label for="start_availability" class="col-form-label col-form-label-sm">Date de début</label>
</div>
<div class="col-sm-8">
<div class="col-sm-8 col-md-5 col-lg-3">
<DateTimeInput class="form-control form-control-sm" id="start_availability" bind:date={work.start_availability} />
</div>
</div>
@ -111,7 +159,7 @@
<div class="col-sm-3 text-sm-end">
<label for="end_availability" class="col-form-label col-form-label-sm">Date de fin</label>
</div>
<div class="col-8">
<div class="col-sm-8 col-md-5 col-lg-3">
<DateTimeInput class="form-control form-control-sm" id="end_availability" bind:date={work.end_availability} />
</div>
</div>

View file

@ -0,0 +1,192 @@
<script>
import { createEventDispatcher } from 'svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher();
export let work;
let gradesP = null;
let gradationStatus = {};
let stats = {"mean": 0, "min": 999, "max": 0};
let chgrade = {grade: null, modal: null};
$: refresh_grades(work);
function refresh_grades(w) {
gradesP = w.getGrades();
gradesP.then((grades) => {
if (grades.length <= 0) return;
let sum = 0;
for (const grade of grades) {
if (!gradationStatus[grade.id])
gradationStatus[grade.id] = grade.gradationStatus();
sum += grade.score;
if (stats.min > grade.score && grade.comment != "- Non rendu -") stats.min = grade.score;
if (stats.max < grade.score) stats.max = grade.score;
}
stats.mean = sum / grades.length;
});
}
async function addMissingStudents(w) {
await w.addMissingGrades();
refresh_grades(w);
}
</script>
<div class="d-flex justify-content-between align-items-center">
<h3 class="mt-3">
Notes
<small class="text-muted">
{#if stats.mean > 0}(moyenne&nbsp;: {Math.round(stats.mean*100)/100}, min&nbsp;: {stats.min}, max&nbsp;: {stats.max}){/if}
</small>
</h3>
<div>
<button
class="btn btn-outline-primary"
title="Afficher le résumé par étapes"
on:click={() => dispatch("switch_steps")}
>
<i class="bi bi-bar-chart-steps"></i>
</button>
<button
class="btn btn-outline-info"
title="Ajouter les étudiants manquant"
on:click={() => addMissingStudents(work)}
>
<i class="bi bi-people"></i>
</button>
<button
class="btn btn-light"
title="Rafraîchir l'affichage des notes"
on:click={() => refresh_grades(work)}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
<div class="card mt-3 mb-5">
{#await gradesP}
<div class="text-center my-5">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement des notes &hellip;</span>
</div>
{:then grades}
<table class="table table-hover table-striped table-sm mb-0">
<thead>
<tr>
<th>Login</th>
<th>Note</th>
<th>Commentaire</th>
<th>Date de la note</th>
</tr>
</thead>
<tbody>
{#if !grades}
<div class="text-center">
Aucune note n'a encore été envoyée pour ce travail.
</div>
{:else}
{#each grades as grade, gid (grade.id)}
<tr>
<td>
<a href="users/{grade.id_user}">{grade.login}</a>
</td>
<td><ScoreBadge score={grade.score} /></td>
<td>{#if grade.comment}{grade.comment}{:else}-{/if}</td>
<td>{grade.date}</td>
<td>
<a
href="/api/users/{grade.id_user}/works/{work.id}/grades/{grade.id}/traces"
target="_blank"
class="btn btn-sm btn-outline-info mr-1"
title="Voir le détail de la notation"
>
<i class="bi bi-list-check"></i>
</a>
<a
href="/api/users/{grade.id_user}/works/{work.id}/grades/{grade.id}/forge"
target="_blank"
class="btn btn-sm btn-outline-primary mr-1"
title="Voir le contenu du dépôt lié"
>
<i class="bi bi-git"></i>
</a>
{#if gradationStatus[grade.id]}
{#await gradationStatus[grade.id]}
<button
class="btn btn-sm btn-outline-success mr-1"
title="Relancer la notation"
on:click={() => { grade.redoGradation().then(() => gradationStatus[grade.id] = grade.gradationStatus()); }}
>
<div class="spinner-border spinner-border-sm" role="status"></div>
</button>
{:then status}
<button
class="btn btn-sm mr-1"
class:btn-success={status.status == "success"}
class:btn-danger={status.status == "failure"}
class:btn-outline-danger={status.status == "killed"}
class:btn-outline-warning={status.status == "pending" || status.status == "running"}
title="Relancer la notation"
on:click={() => { grade.redoGradation(); gradationStatus[grade.id] = null; }}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
{/await}
{/if}
<button
class="btn btn-sm btn-primary mr-1"
title="Changer la note"
on:click={() => { chgrade = { grade, modal: new bootstrap.Modal(document.getElementById('chgradeModal'))}; chgrade.modal.show(); }}
>
<i class="bi bi-pencil"></i>
</button>
<button
class="btn btn-sm btn-danger mr-1"
title="Supprimer la note"
on:click={() => { grade.delete().then(() => refresh_grades(work)); }}
>
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
{/each}
{/if}
</tbody>
</table>
{/await}
</div>
<div class="modal fade" tabindex="-1" id="chgradeModal">
<div class="modal-dialog">
<form class="modal-content" on:submit|preventDefault={() => {chgrade.modal.hide(); try { chgrade.grade.save().then(() => refresh_grades(work)); } catch(err) { ToastsStore.addToast({color: "danger", title: "Impossible de changer la note", msg: err}) };}}>
<div class="modal-header">
<h5 class="modal-title">Changer la note {#if chgrade.grade}de {chgrade.grade.login}{/if}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{#if chgrade.grade}
<div class="form-group row mb-2">
<label class="col-2 col-form-label" for="new-grade">Note</label>
<!-- svelte-ignore a11y-autofocus -->
<input type="number" class="form-control col" id="new-grade" autofocus placeholder="15" bind:value={chgrade.grade.score}>
</div>
<div class="form-group row mb-2">
<label class="col-2 col-form-label" for="new-comment">Commentaire</label>
<input class="form-control col" id="new-comment" bind:value={chgrade.grade.comment}>
</div>
{/if}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">
Changer la note
</button>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,214 @@
<script>
import { createEventDispatcher } from 'svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
const dispatch = createEventDispatcher();
export let work;
let gradesP = null;
let grade_idx = {};
let gradationStatus = {};
let stats = [];
$: refresh_grades(work);
function refresh_grades(w) {
gradesP = w.getGrades();
gradesP.then((grades) => {
if (grades.length <= 0) return;
for (const grade of grades) {
grade_idx[grade.id] = grade;
if (!gradationStatus[grade.id]) {
gradationStatus[grade.id] = grade.gradationStatus();
gradationStatus[grade.id].then((status) => {
for (const istage in status.stages) {
const stage = status.stages[istage];
if (stats.length <= istage) {
stats.push({
arch: stage.arch,
name: stage.name,
number: stage.number,
status: [],
steps: [],
});
}
stats[istage].status.push(stage.status);
for (const istep in stage.steps) {
const step = stage.steps[istep];
if (stats[istage].steps.length <= istep) {
stats[istage].steps.push({
name: step.name,
number: step.number,
status: [],
});
}
stats[istage].steps[istep].status.push(step.status);
}
}
stats = stats;
});
}
}
});
}
let view_step = null;
</script>
<div class="d-flex justify-content-between align-items-center">
<h3 class="mt-3">
Réussite des étapes
</h3>
<div>
<button
class="btn btn-primary"
title="Afficher le résumé par étapes"
on:click={() => dispatch("switch_steps")}
>
<i class="bi bi-bar-chart-steps"></i>
</button>
<button
class="btn btn-light"
title="Rafraîchir l'affichage des notes"
on:click={() => refresh_grades(work)}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
{#each stats as stage, istage}
<h5>
{stage.name}
<small>{stage.arch}</small>
</h5>
<div class="row row-cols-5">
{#each stage.steps as step, istep}
<div class="col">
<div class="card mb-3">
<div class="card-body fw-bolder text-truncate" title={step.name}>
{step.number}. {step.name}
</div>
<div
class="card-footer text-center"
class:bg-success={step.status.filter((e) => e == "success").length/step.status.length > 0.5}
on:click={() => view_step = {istage, istep, status: "success"}}
>
<i class="bi bi-check me-2 fw-bolder"></i>
{step.status.filter((e) => e == "success").length}
({Math.trunc(step.status.filter((e) => e == "success").length*100/step.status.length)}&nbsp;%)
</div>
<div
class="card-footer text-center"
class:bg-danger={step.status.filter((e) => e == "failure").length/step.status.length >= 0.5}
on:click={() => view_step = {istage, istep, status: "failure"}}
>
<i class="bi bi-x me-2 fw-bolder"></i>
{step.status.filter((e) => e == "failure").length}
({Math.trunc(step.status.filter((e) => e == "failure").length*100/step.status.length)}&nbsp;%)
</div>
{#if step.status.filter((e) => e == "skipped").length > 0}
<div
class="card-footer text-center"
class:fw-bold={step.status.filter((e) => e == "skipped").length/step.status.length >= 0.5}
on:click={() => view_step = {istage, istep, status: "skipped"}}
>
<i class="bi bi-skip-end me-2 fw-bolder"></i>
{step.status.filter((e) => e == "skipped").length}
({Math.trunc(step.status.filter((e) => e == "skipped").length*100/step.status.length)}&nbsp;%)
</div>
{/if}
</div>
</div>
{/each}
</div>
{/each}
{#if view_step}
<h3>
Étudiants correspondant
<small class="text-muted">
{"{"}
{stats[view_step.istage].name}
<i class="bi bi-arrow-right"></i>
<em>{stats[view_step.istage].steps[view_step.istep].name}</em>
"{view_step.status}"
{"}"}
</small>
</h3>
<div class="row row-cols-6">
{#each Object.keys(gradationStatus) as gsi}
{#await gradationStatus[gsi] then gs}
{#if gs.stages[view_step.istage] && gs.stages[view_step.istage].steps[view_step.istep] && gs.stages[view_step.istage].steps[view_step.istep].status == view_step.status}
<div class="col">
<div class="card mb-3">
<div
class="card-header text-monospace text-truncate"
title={grade_idx[gsi].login}
>
<a href="/users/{grade_idx[gsi].id_user}">
{grade_idx[gsi].login}
</a>
</div>
<ul class="list-group list-group-flush">
{#each gs.stages[view_step.istage].steps as step}
<li
class="list-group-item text-truncate p-2"
class:bg-success={step.status == "success"}
class:bg-light={step.status == "skipped"}
class:bg-danger={step.status == "failure"}
class:bg-warning={step.status == "pending" || step.status == "running"}
class:bg-info={step.status == "killed"}
>
<a
href="/api/users/{grade_idx[gsi].id_user}/works/{work.id}/grades/{grade_idx[gsi].id}/traces/{gs.stages[view_step.istage].number}/{step.number}"
target="_blank"
title="Voir le détail de cette étape"
>
{step.number}.
</a>
{step.name}
</li>
{/each}
</ul>
<div
class="card-footer d-flex justify-content-around align-items-center px-0"
>
<ScoreBadge score={grade_idx[gsi].score} />
<a
href="/api/users/{grade_idx[gsi].id_user}/works/{work.id}/grades/{grade_idx[gsi].id}/traces"
target="_blank"
class="btn btn-sm btn-outline-info"
title="Voir le détail de la notation"
>
<i class="bi bi-list-check"></i>
</a>
<a
href="/api/users/{grade_idx[gsi].id_user}/works/{work.id}/grades/{grade_idx[gsi].id}/forge"
target="_blank"
class="btn btn-sm btn-outline-primary"
title="Voir le contenu du dépôt lié"
>
<i class="bi bi-git"></i>
</a>
<button
class="btn btn-sm btn-outline-success"
title="Relancer la notation"
on:click={() => { grade_idx[gsi].redoGradation(); gradationStatus[gsi] = null; }}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
</div>
{/if}
{/await}
{/each}
</div>
{/if}

View file

@ -0,0 +1,39 @@
<script>
import { user } from '$lib/stores/user';
import DateFormat from '$lib/components/DateFormat.svelte';
import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
export let work;
export let my_submission;
</script>
<dl style="columns: 3">
<dt>Date de début</dt>
<dd><DateFormat date={new Date(work.start_availability)} dateStyle="medium" timeStyle="medium" /></dd>
<dt>Date de fin</dt>
<dd><DateFormat date={new Date(work.end_availability)} dateStyle="medium" timeStyle="medium" /></dd>
{#if work.submission_url != "-"}
<dt>Rendu&nbsp;?</dt>
<dd>
{#if work.submission_url}
<SubmissionStatus work={w} user={$user} />
{:else}
{#await my_submission}
<div class="spinner-grow spinner-grow-sm mx-1" role="status"></div>
{:then submission}
<i
class="bi bi-check-circle text-success"
title="Oui !"
></i>
<DateFormat date={new Date(submission.date)} dateStyle="medium" timeStyle="medium" />
{:catch}
<i
class="bi bi-x-circle text-danger"
title="Pas de rendu trouvé"
></i>
Non
{/await}
{/if}
</dd>
{/if}
</dl>

View file

@ -1,10 +1,10 @@
<script>
import { createEventDispatcher } from 'svelte';
import BuildState from '../components/BuildState.svelte';
import DateFormat from '../components/DateFormat.svelte';
import { WorkRepository, getRemoteRepositories, getRepositories } from '../lib/repositories';
import { ToastsStore } from '../stores/toasts';
import BuildState from '$lib/components/BuildState.svelte';
import DateFormat from '$lib/components/DateFormat.svelte';
import { WorkRepository, getRemoteRepositories, getRepositories } from '$lib/repositories';
import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher();
@ -159,12 +159,12 @@
<span>Récupération de vos dépôts GitLab &hellip;</span>
</div>
{:then rrepos}
<select class="form-select col" disabled={readonly} bind:value={repo_used.uri}>
{#each rrepos as r (r.Id)}
<select id="repolist" class="form-select col" disabled={readonly} bind:value={repo_used.uri}>
{#each rrepos as r (r.ssh_url_to_repo)}
<option value={r.ssh_url_to_repo}>{r.path_with_namespace}</option>
{/each}
</select>
<label>Dépôt GitLab pour ce travail&nbsp;:</label>
<label for="repolist">Dépôt GitLab pour ce travail&nbsp;:</label>
<button
type="submit"
class="mt-2 btn btn-primary"
@ -185,6 +185,52 @@
>
<i class="bi bi-arrow-clockwise"></i>
</button>
<button
type="button"
class="mt-2 btn btn-light"
on:click={() => {repo_used.uri = ""; repo_used.modal = new bootstrap.Modal(document.getElementById('customRepoModal')); repo_used.modal.show();}}
disable={submitInProgress || readonly || !repo_used || !repo_used.uri}
>
Utiliser un autre dépôt
</button>
</form>
{/if}
{/await}
{#if repo_used}
<div class="modal fade" tabindex="-1" id="customRepoModal">
<div class="modal-dialog modal-lg">
<form class="modal-content" on:submit|preventDefault={() => {repo_used.modal.hide(); delete repo_used.modal; submitWorkRepository()}}>
<div class="modal-header">
<h5 class="modal-title">Sélection de dépôt</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>
Vous pouvez utiliser un dépôt hébergé sur une
autre forge, qu'elle soit publique ou
personnelle. Recopiez pour cela l'adresse du dépôt
dans le champ ci-dessous.
</p>
<div class="form-group mb-3">
<label class="form-label" for="repo-address">Adresse du dépôt</label>
<!-- svelte-ignore a11y-autofocus -->
<input class="form-control" id="repo-address" autofocus placeholder="git@git.mydomain.net:path/to/repo.git" bind:value={repo_used.uri}>
</div>
<p>
Assurez-vous bien que votre dépôt <strong>n'est pas public</strong>
et d'avoir <strong>ajouté une clef de déploiement</strong>
à votre dépôt&nbsp;:
</p>
<pre>
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINfzEnTiqwC4EeUG5EqfO0mLCygLU0HDiHTYgroNwjtT</pre>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">
Définir l'adresse de mon dépôt
</button>
</div>
</form>
</div>
</div>
{/if}

19
ui/src/lib/gradation.js Normal file
View file

@ -0,0 +1,19 @@
export async function getGradationRepositories() {
let url = '/api/gradation_repositories';
const res = await fetch(url, {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
export async function syncGradationRepositories() {
let url = '/api/gradation_repositories/sync';
const res = await fetch(url, {method: 'post', headers: {'Accept': 'application/json'}})
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}

70
ui/src/lib/grades.js Normal file
View file

@ -0,0 +1,70 @@
export class Grade {
constructor(res) {
if (res) {
this.update(res);
}
}
update({ id, login, id_user, id_work, date, score, comment }) {
this.id = id;
this.login = login;
this.id_user = id_user;
this.id_work = id_work;
this.date = date;
this.score = score;
this.comment = comment;
}
async save() {
const res = await fetch(`api/works/${this.id_work}/grades/${this.id}`, {
method: 'PUT',
headers: {'Accept': 'application/json'},
body: JSON.stringify(this),
});
if (res.status == 200) {
const data = await res.json()
this.update(data);
return data;
} else {
throw new Error((await res.json()).errmsg);
}
}
async delete() {
if (this.id) {
const res = await fetch(`api/works/${this.id_work}/grades/${this.id}`, {
method: 'DELETE',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return true;
} else {
throw new Error((await res.json()).errmsg);
}
}
}
async redoGradation() {
const res = await fetch(this.id_user?`api/users/${this.id_user}/works/${this.id_work}/grades/${this.id}/traces`:`api/works/${this.id_work}/grades/${this.id}/traces`, {
method: 'POST',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
async gradationStatus() {
const res = await fetch(this.id_user?`api/users/${this.id_user}/works/${this.id_work}/grades/${this.id}/status`:`api/works/${this.id_work}/grades/${this.id}/status`, {
method: 'GET',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
}

View file

@ -62,8 +62,10 @@ export class Question {
this.kind = kind;
}
async getProposals() {
const res = await fetch(`api/questions/${this.id}/proposals`, {
async getProposals(secret) {
let url = `/questions/${this.id}/proposals`;
if (secret) url = `/s/surveys/${this.id_survey}` + url + `?secret=${secret}`;
const res = await fetch('api' + url, {
method: 'GET',
headers: {'Accept': 'application/json'},
});
@ -91,8 +93,10 @@ export class Question {
}
}
async getResponses() {
const res = await fetch(`api/surveys/${this.id_survey}/questions/${this.id}/responses`, {
async getResponses(secret) {
let url = `/surveys/${this.id_survey}/questions/${this.id}/responses`;
if (secret) url = `/s` + url + `?secret=${secret}`;
const res = await fetch('api' + url, {
method: 'GET',
headers: {'Accept': 'application/json'},
});
@ -161,3 +165,17 @@ export async function getQuestions(sid) {
throw new Error((await res.json()).errmsg);
}
}
export async function getSharedQuestions(sid, secret) {
const res = await fetch(`api/s/surveys/${sid}/questions?secret=${secret}`, {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
const data = await res.json();
if (data === null) {
return [];
} else {
return (data).map((q) => new Question(q))
}
} else {
throw new Error((await res.json()).errmsg);
}
}

View file

@ -15,8 +15,14 @@ export class WorkRepository {
this.already_used = already_used == true;
}
async delete() {
const res = await fetch(this.id_work?`api/works/${this.id_work}/repositories/${this.id}`:`api/repositories/${this.id}`, {
async delete(userid) {
let url = this.id_work?`works/${this.id_work}/repositories/${this.id}`:`repositories/${this.id}`;
if (userid) {
url = `users/${userid}/` + url;
}
const res = await fetch("api/" + url, {
method: 'DELETE',
headers: {'Accept': 'application/json'}
});
@ -55,11 +61,11 @@ export class WorkRepository {
}
}
async retrieveWork(tag) {
async retrieveWork(admin_struct) {
const res = await fetch(this.id_work?`api/works/${this.id_work}/repositories/${this.id}/trigger`:`api/repositories/${this.id}/trigger`, {
method: 'POST',
headers: {'Accept': 'application/json'},
body: !tag || tag.length == 0?null:JSON.stringify(tag)
body: !admin_struct?{}:JSON.stringify(admin_struct)
});
if (res.status == 200) {
const data = await res.json();
@ -70,6 +76,30 @@ export class WorkRepository {
}
}
async runGradation() {
const res = await fetch(this.id_work?`api/works/${this.id_work}/repositories/${this.id}/gradation`:`api/repositories/${this.id}/gradation`, {
method: 'POST',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
async gradationStatus() {
const res = await fetch(this.id_work?`api/works/${this.id_work}/repositories/${this.id}/gradation_status`:`api/repositories/${this.id}/gradation_status`, {
method: 'GET',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
async save(user) {
let url = this.id?`repositories/${this.id}`:'repositories';

View file

@ -8,14 +8,10 @@ function createUserStore() {
set: (auth) => {
update((m) => auth);
},
update: (res_auth, cb=null) => {
update: (res_auth) => {
if (res_auth.status === 200) {
res_auth.json().then((auth) => {
update((m) => (Object.assign(m?m:{}, auth)));
if (cb) {
cb(my);
}
});
} else if (res_auth.status >= 400 && res_auth.status < 500) {
update((m) => (null));
@ -24,4 +20,14 @@ function createUserStore() {
};
}
export async function refresh_auth() {
const res = await fetch('api/auth', {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
const auth = await res.json();
user.set(auth);
} else {
user.set(null);
}
}
export const user = createUserStore();

View file

@ -11,8 +11,9 @@ export class Survey {
}
}
update({ id, title, promo, group, shown, direct, corrected, start_availability, end_availability }) {
update({ id, id_category, title, promo, group, shown, direct, corrected, start_availability, end_availability }) {
this.id = id;
this.id_category = id_category;
this.title = title;
this.promo = promo;
this.group = group;
@ -72,6 +73,18 @@ export class Survey {
}
}
async share() {
const res = await fetch(`api/surveys/${this.id}/shares`, {
method: 'POST',
headers: {'Accept': 'application/json'}
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
async save() {
const res = await fetch(this.id?`api/surveys/${this.id}`:'api/surveys', {
method: this.id?'PUT':'POST',
@ -104,31 +117,35 @@ export class Survey {
for (const q of questions) {
const oldQuestionId = q.id;
// This will create a new question with the same parameters
delete q.id;
// Also alter id_survey
q.id_survey = response.id;
q.save().then((question) => {
q.id = oldQuestionId;
// Now recopy proposals
if (q.kind == "mcq" || q.kind == "ucq") {
q.getProposals().then((proposals) => {
for (const p of proposals) {
delete p.id;
p.id_question = question.id;
p.save();
}
});
// This save will create
const question = await q.save();
// Revert to the old question ID to perform the next retrievals
q.id = oldQuestionId;
// Now recopy proposals
if (q.kind == "mcq" || q.kind == "ucq") {
const proposals = await q.getProposals();
for (const p of proposals) {
delete p.id;
p.id_question = question.id;
await p.save();
}
}
// Now recopy correction templates
getCorrectionTemplates(oldQuestionId).then((cts) => {
for (const ct of cts) {
delete ct.id;
ct.id_question = question.id;
ct.save();
}
});
});
// Now recopy correction templates
const cts = await getCorrectionTemplates(oldQuestionId);
for (const ct of cts) {
delete ct.id;
ct.id_question = question.id;
ct.save();
}
}
return response;
@ -185,3 +202,12 @@ export async function getSurvey(sid) {
throw new Error((await res.json()).errmsg);
}
}
export async function getSharedSurvey(sid, secret) {
const res = await fetch(`api/s/surveys/${sid}?secret=${secret}`, {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
return new Survey(await res.json());
} else {
throw new Error((await res.json()).errmsg);
}
}

View file

@ -19,6 +19,18 @@ export async function getUsers(promo, group) {
}
}
export async function anonOldAccounts() {
const res = await fetch('api/users', {
method: 'PATCH',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return await res.json()
} else {
throw new Error((await res.json()).errmsg);
}
}
export class User {
constructor(res) {
if (res) {

View file

@ -1,3 +1,5 @@
import { Grade } from '$lib/grades';
export class Work {
constructor(res) {
this.kind = "w";
@ -6,8 +8,9 @@ export class Work {
}
}
update({ id, title, promo, group, shown, tag, description, descr_raw, submission_url, corrected, start_availability, end_availability }) {
update({ id, id_category, title, promo, group, shown, tag, description, descr_raw, submission_url, gradation_repo, corrected, start_availability, end_availability }) {
this.id = id;
this.id_category = id_category;
this.title = title;
this.promo = promo;
this.group = group;
@ -16,6 +19,7 @@ export class Work {
this.description = description;
this.descr_raw = descr_raw;
this.submission_url = submission_url;
this.gradation_repo = gradation_repo;
this.corrected = corrected;
if (this.start_availability != start_availability) {
this.start_availability = start_availability;
@ -91,6 +95,32 @@ export class Work {
}
}
async stopTests() {
if (this.id) {
const res = await fetch(`api/works/${this.id}/tests`, {
method: 'DELETE',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return true;
} else {
throw new Error((await res.json()).errmsg);
}
}
}
async addMissingGrades() {
const res = await fetch(`api/works/${this.id}/grades`, {
method: 'PATCH',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return (await res.json()).map((g) => new Grade(g));
} else {
throw new Error((await res.json()).errmsg);
}
}
async getSubmission(uid) {
const res = await fetch(uid?`api/users/${uid}/works/${this.id}/submission`:`api/works/${this.id}/submission`, {
headers: {'Accept': 'application/json'}
@ -102,13 +132,25 @@ export class Work {
}
}
async getMyTraces() {
const res = await fetch(`api/works/${this.id}/traces`, {
method: 'GET',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return await res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
async getGrades() {
const res = await fetch(`api/works/${this.id}/grades`, {
method: 'GET',
headers: {'Accept': 'application/json'},
});
if (res.status == 200) {
return await res.json();
return (await res.json()).map((g) => new Grade(g));
} else {
throw new Error((await res.json()).errmsg);
}

16
ui/src/routes/+layout.js Normal file
View file

@ -0,0 +1,16 @@
import { refresh_auth, user } from '$lib/stores/user';
export const ssr = false;
let refresh_interval_auth = null;
export async function load({ url }) {
refresh_interval_auth = setInterval(refresh_auth, Math.floor(Math.random() * 200000) + 200000);
refresh_auth();
const rroutes = url.pathname.split('/');
return {
rroute: rroutes.length>1?rroutes[1]:'',
};
}

View file

@ -1,51 +1,9 @@
<script context="module">
import { user } from '../stores/user';
let stop_refresh = false;
let refresh_interval_auth = null;
async function refresh_auth(cb=null, interval=null) {
if (refresh_interval_auth)
clearInterval(refresh_interval_auth);
if (interval === null) {
interval = Math.floor(Math.random() * 200000) + 200000;
}
if (stop_refresh) {
return;
}
refresh_interval_auth = setInterval(refresh_auth, interval);
const res = await fetch('api/auth', {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
const auth = await res.json();
user.set(auth);
} else {
user.set(null);
}
}
export async function load({ props, stuff, url }) {
refresh_auth();
const rroutes = url.pathname.split('/');
return {
props: {
...props,
rroute: rroutes.length>1?rroutes[1]:'',
},
stuff: {
...stuff,
refresh_auth,
}
};
}
</script>
<script>
import AuthButton from '../components/AuthButton.svelte';
import Toaster from '../components/Toaster.svelte';
import AuthButton from '$lib/components/AuthButton.svelte';
import Toaster from '$lib/components/Toaster.svelte';
import { refresh_auth, user } from '$lib/stores/user';
export let rroute = '';
export let data;
function switchAdminMode() {
var tmp = $user.is_admin;
@ -71,6 +29,11 @@
<title>ЕРІТА: MCQ and others courses related stuff</title>
</svelte:head>
{#if $user && $user.banner}
<div class="bg-danger text-white text-center py-1 fw-bolder">
{$user.banner}
</div>
{/if}
{#if isSRS}
<div style="position: fixed; bottom: 20px; right: 20px; z-index: -1; background-image: url('img/srstamps.png'); background-size: cover; width: 125px; height: 125px;"></div>
{/if}
@ -86,7 +49,11 @@
<div class="collapse navbar-collapse" id="loggedMenu">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="adlin" target="_self">AdLin</a>
{#if $user && $user.promo != $user.current_promo}
<a class="nav-link" href="adlin/{$user.promo}" target="_self">AdLin</a>
{:else}
<a class="nav-link" href="adlin" target="_self">AdLin</a>
{/if}
</li>
{#if isSRS}
<li class="nav-item">
@ -94,19 +61,25 @@
</li>
{/if}
<li class="nav-item">
<a class="nav-link" class:active={rroute === 'surveys'} href="surveys">
<a class="nav-link" class:active={data.rroute === 'surveys'} href="surveys">
Questionnaires
</a>
</li>
<li class="nav-item">
<a class="nav-link" class:active={rroute === 'works'} href="works">
<a class="nav-link" class:active={data.rroute === 'works'} href="works">
Travaux
</a>
</li>
{#if $user && $user.is_admin}
<li class="nav-item"><a class="nav-link" class:active={rroute === 'users'} href="users">Étudiants</a></li>
<li class="nav-item"><a class="nav-link" class:active={data.rroute === 'users'} href="users">Étudiants</a></li>
{/if}
<li class="nav-item"><a class="nav-link" href="virli" target="_self">VIRLI</a></li>
<li class="nav-item">
{#if $user && $user.promo != $user.current_promo}
<a class="nav-link" href="virli/{$user.promo}" target="_self">VIRLI</a>
{:else}
<a class="nav-link" href="virli" target="_self">VIRLI</a>
{/if}
</li>
</ul>
<ul class="navbar-nav ms-auto">
@ -125,13 +98,12 @@
</li>
{/if}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#nav" role="button" aria-expanded="false">
<img class="rounded-circle" src="//photos.cri.epita.fr/square/{$user.login}" alt="Menu" style="margin: -0.75em 0; max-height: 2.5em; border: 2px solid white;">
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" class:active={rroute === 'keys'} href="keys">Clef PGP</a></li>
<li><a class="dropdown-item" class:active={rroute === 'help'} href="help">Besoin d'aide&nbsp;?</a></li>
<li><a class="dropdown-item" class:active={rroute === 'bug-bounty'} href="bug-bounty">Bug Bounty</a></li>
<li><a class="dropdown-item" class:active={data.rroute === 'keys'} href="keys">Clef PGP</a></li>
<li><a class="dropdown-item" class:active={data.rroute === 'bug-bounty'} href="bug-bounty">Bug Bounty</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<button class="dropdown-item" on:click={disconnectCurrentUser}>

View file

@ -1,9 +1,9 @@
<script lang="ts">
import { user } from '../stores/user';
import { getUser, getUserNeedingHelp } from '../lib/users';
import DateFormat from '../components/DateFormat.svelte';
import SurveyList from '../components/SurveyList.svelte';
import ValidateSubmissions from '../components/ValidateSubmissions.svelte';
import { user } from '$lib/stores/user';
import { getUser, getUserNeedingHelp } from '$lib/users';
import DateFormat from '$lib/components/DateFormat.svelte';
import SurveyList from '$lib/components/SurveyList.svelte';
import ValidateSubmissions from '$lib/components/ValidateSubmissions.svelte';
let direct = null;

View file

@ -2,9 +2,9 @@
import { goto } from '$app/navigation';
import { page } from '$app/stores'
import AuthButton from '../components/AuthButton.svelte';
import { ToastsStore } from '../stores/toasts';
import { user } from '../stores/user';
import AuthButton from '$lib/components/AuthButton.svelte';
import { ToastsStore } from '$lib/stores/toasts';
import { user } from '$lib/stores/user';
let auth = { username: "", password: "" };
let pleaseWait = false;
@ -55,6 +55,7 @@
<form class="col" on:submit|preventDefault={logmein}>
<h2>Accès à votre compte</h2>
<div class="form-floating mb-3">
<!-- svelte-ignore a11y-autofocus -->
<input type="text" class="form-control" id="login" bind:value={auth.username} placeholder="xavier.login" autofocus>
<label for="login">CRI login</label>
</div>

View file

@ -59,12 +59,28 @@
<div class="alert alert-warning d-flex">
<i class="bi bi-exclamation-triangle me-3"></i>
<span>
À toute fin utile, l'usage et la non-divulgation d'une vulnérabilité sont <a href="https://www.legifrance.gouv.fr/codes/id/LEGISCTA000006149839/" target="_blank">sanctionnables</a>.
À toute fin utile, l'usage et la non-divulgation d'une vulnérabilité sont <a href="https://www.legifrance.gouv.fr/codes/id/LEGISCTA000006149839/" target="_blank" rel="noreferrer">sanctionnables</a>.
</span>
</div>
<h3 class="mt-5 mb-3">Hall of Fame</h3>
<div class="card mb-3">
<div class="card-header">
L'accès aux questionnaires n'était pas filtré selon les groupes ou les promos.
<span class="badge bg-success shadow-lg">+2&nbsp;pts</span>
</div>
<div class="card-body">
<div class="row row-cols-6">
<img class="img-thumbnail" src="//photos.cri.epita.fr/francois.dautreme" alt="francois.dautreme">
</div>
<p class="card-text mt-3">
Divulguée et corrigée le 19 novembre 2022.
<a href="https://git.nemunai.re/teach/atsebay.t/commit/f675047ce8f6636aa45336b56c069172330b050f" target="_blank" rel="noreferrer">Commit</a>
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
Il était toujours possible de répondre aux questionnaires après l'heure de clôture.
@ -81,7 +97,7 @@
</div>
<p class="card-text mt-3">
Divulguée et corrigée le 19 novembre 2021.
<a href="https://git.nemunai.re/srs/atsebay.t/commit/5c53d2eaea9e7233bc8a08de2f40c040c0700c3e" target="_blank">Commit</a>
<a href="https://git.nemunai.re/teach/atsebay.t/commit/5c53d2eaea9e7233bc8a08de2f40c040c0700c3e" target="_blank" rel="noreferrer">Commit</a>
</p>
</div>
</div>

View file

@ -0,0 +1,70 @@
<script>
import { goto } from '$app/navigation';
import { user } from '$lib/stores/user';
import { getCategories } from '$lib/categories';
import { getPromos } from '$lib/users';
function showCategory(category) {
goto(`categories/${category.id}`)
}
let filterPromo = "";
</script>
{#if $user && $user.is_admin}
<a href="categories/new" class="btn btn-primary ml-1 float-end" title="Ajouter une catégorie">
<i class="bi bi-plus"></i>
</a>
{#await getPromos() then promos}
<div class="float-end me-2">
<select class="form-select" bind:value={filterPromo}>
<option value="">-</option>
{#each promos as promo, pid (pid)}
<option value={promo}>{promo}</option>
{/each}
</select>
</div>
{/await}
{/if}
<h2>
Catégories // cours
</h2>
{#await getCategories()}
<div class="text-center">
<div class="spinner-border text-danger mx-3" role="status"></div>
<span>Chargement des catégories &hellip;</span>
</div>
{:then categories}
<table class="table table-sm table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Nom</th>
<th>Promo</th>
<th>Étendre</th>
</tr>
</thead>
<tbody>
{#each categories.filter((c) => (filterPromo === "" || filterPromo === c.promo)) as c (c.id)}
<tr>
<td>{c.id}</td>
<td>
<a href="categories/{c.id}">{c.label}</a>
</td>
<td>{c.promo}</td>
<td>
<span
class="badge"
class:bg-success={c.expand}
class:bg-danger={!c.expand}
>
{c.expand?"Oui":"Non"}
</span>
</td>
</tr>
{/each}
</tbody>
</table>
{/await}

View file

@ -0,0 +1,5 @@
export async function load({ params }) {
return {
cid: params.cid,
};
}

View file

@ -0,0 +1,25 @@
<script>
import { goto } from '$app/navigation';
import { user } from '$lib/stores/user';
import CategoryAdmin from '$lib/components/CategoryAdmin.svelte';
import { Category, getCategory } from '$lib/categories';
export let data;
let categoryP = null;
$: categoryP = getCategory(data.cid);
</script>
{#await categoryP then category}
<div class="d-flex align-items-center">
<h2>
<a href="categories/" class="text-muted" style="text-decoration: none">&lt;</a>
{category.label}
</h2>
</div>
{#if $user && $user.is_admin}
<CategoryAdmin {category} on:saved={(e) => { goto(`categories/`)}} />
{/if}
{/await}

View file

@ -0,0 +1,20 @@
<script>
import { goto } from '$app/navigation';
import { user } from '$lib/stores/user';
import CategoryAdmin from '$lib/components/CategoryAdmin.svelte';
import { Category } from '$lib/categories';
let category = new Category();
</script>
<div class="d-flex align-items-center">
<h2>
<a href="categories/" class="text-muted" style="text-decoration: none">&lt;</a>
Nouvelle catégorie
</h2>
</div>
{#if $user && $user.is_admin}
<CategoryAdmin {category} on:saved={(e) => { goto(`categories/${e.detail.id}`)}} />
{/if}

View file

@ -131,7 +131,7 @@
<h3>Mesure d'audience</h3>
<p>
Le site <code>lessons.nemunai.re</code> utilise un outil de mesure d'audience&nbsp;: <a href="https://umami.is" target="_blank">Umami</a>.
Le site <code>lessons.nemunai.re</code> utilise un outil de mesure d'audience&nbsp;: <a href="https://umami.is" target="_blank" rel="noreferrer">Umami</a>.
Cet outil collecte des informations sur les pages visitées en anonymisant les données personnelles (l'IP notamment).
</p>

View file

@ -0,0 +1,5 @@
<script>
import StudentGrades from '$lib/components/StudentGrades.svelte';
</script>
<StudentGrades />

View file

@ -1,17 +0,0 @@
<script context="module">
export async function load({ params }) {
return {
props: {
promo: params.promo,
}
};
}
</script>
<script>
import StudentGrades from '../../components/StudentGrades.svelte';
export let promo;
</script>
<StudentGrades {promo} />

View file

@ -0,0 +1,5 @@
export async function load({ params }) {
return {
promo: parseInt(params.promo),
};
}

View file

@ -0,0 +1,7 @@
<script>
import StudentGrades from '$lib/components/StudentGrades.svelte';
export let data;
</script>
<StudentGrades promo={data.promo} />

View file

@ -0,0 +1,6 @@
export async function load({ params }) {
return {
promo: parseInt(params.promo),
cid: parseInt(params.cid),
};
}

View file

@ -0,0 +1,7 @@
<script>
import StudentGrades from '$lib/components/StudentGrades.svelte';
export let data;
</script>
<StudentGrades promo={data.promo} category={data.cid} />

View file

@ -1,5 +0,0 @@
<script>
import StudentGrades from '../../components/StudentGrades.svelte';
</script>
<StudentGrades />

View file

@ -1,6 +1,6 @@
<script>
import { user } from '../stores/user';
import { ToastsStore } from '../stores/toasts';
import { user } from '$lib/stores/user';
import { ToastsStore } from '$lib/stores/toasts';
function needhelp() {
fetch('api/help', {
@ -44,7 +44,7 @@
<p>
Si tu souhaites me parler d'une situation qui t'a troublé&middot;e, d'un problème que tu rencontres ou me faire une remarque,
n'hésite pas à venir me voir lors d'un cours, par exemple à la pause ou à la fin{#if $user}&nbsp;;
je suis aussi joignable <a href="mailto:nemunaire@nemunai.re" class="umami--click--need-help-mail">par e-mail</a> ou bien <a href="https://matrix.to/#/@nemunaire:nemunai.re" class="umami--click--need-help-matrix">sur Matrix</a> ou Teams{/if}.
je suis aussi joignable <a href="mailto:nemunaire@nemunai.re" data-umami-event="need-help-mail">par e-mail</a> ou bien <a href="https://matrix.to/#/@nemunaire:nemunai.re" data-umami-event="need-help-matrix">sur Matrix</a> ou Teams{/if}.
</p>
{#if $user}
@ -52,7 +52,8 @@
Si tu souhaites juste avoir un peu plus d'attention, soit parce que tu te sens à l'écart, en difficulté ou autre&nbsp;:
<button
type="button"
class="btn btn-sm btn-primary umami--click--need-help"
class="btn btn-sm btn-primary"
data-umami-event="need-help"
on:click={needhelp}
>
Clique ce bouton

View file

@ -1,7 +1,7 @@
<script>
import { deleteKey, getKeys, getKey, Key } from '../lib/key';
import { user } from '../stores/user';
import { ToastsStore } from '../stores/toasts';
import { deleteKey, getKeys, getKey, Key } from '$lib/key';
import { user } from '$lib/stores/user';
import { ToastsStore } from '$lib/stores/toasts';
let keysP = getKeys();
@ -125,7 +125,7 @@ ZWxxdWUgY2hvc2UK ...
Votre clef PGP peut vous servir à sécuriser les échanges de courriers électroniques. Par exemple les courriels officiels sur les listes de diffusion sensibles sont systématiquement signés (pour attester que c'est le responsable du projet qui en est bien à l'origine) ou encore si vous ne voulez pas que des tiers exploitent vos communications et/ou données personnelles (dans ce cas, on chiffre le contenu pour qu'il ne soit lisible que par le(s) destinataire(s) attendus).
</p>
<p>
Avec git, vous pouvez signer chacun de <a href="https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/" target="_blank">vos commits</a>, ou <a href="https://dev.to/shostarsson/how-to-use-pgp-to-sign-your-commits-on-github-gitlab-bitbucket-3dae#fountainpen-sign-tags-using-your-gpg-key" target="_blank">vos tags</a>.<br>
Avec git, vous pouvez signer chacun de <a href="https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/" target="_blank" rel="noreferrer">vos commits</a>, ou <a href="https://dev.to/shostarsson/how-to-use-pgp-to-sign-your-commits-on-github-gitlab-bitbucket-3dae#fountainpen-sign-tags-using-your-gpg-key" target="_blank" rel="noreferrer">vos tags</a>.<br>
Si vous souhaitez contribuer au <a href="https://www.kernel.org/doc/html/latest/process/maintainer-pgp-guide.html">noyau Linux</a> (ou tout autre projet d'envergure), il est nécessaire de signer vos contributions. Cela permet d'éviter <a href="https://www.theregister.com/2021/03/29/php_repository_infected/">un certain nombre d'attaques</a>.
</p>
<p>

View file

@ -0,0 +1,7 @@
export async function load({ url }) {
return {
secret: url.searchParams.get("secret"),
idsurvey: url.searchParams.get("survey"),
exportview_list: url.searchParams.get("graph_list")?false:true,
};
}

View file

@ -0,0 +1,48 @@
<script>
import { getSharedQuestions } from '$lib/questions';
import { getSharedSurvey } from '$lib/surveys';
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
export let data;
let surveyP = null;
$: surveyP = getSharedSurvey(data.idsurvey, data.secret);
</script>
{#await surveyP then survey}
<div class="d-flex align-items-center">
<h2>
{survey.title}
<small class="text-muted">Réponses</small>
</h2>
<SurveyBadge class="ms-2" {survey} />
</div>
{#await getSharedQuestions(survey.id, data.secret)}
<div class="text-center">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement des questions &hellip;</span>
</div>
{:then questions}
{#each questions as question (question.id)}
<h3>{question.title}</h3>
{#if question.kind == "text" || (data.exportview_list && question.kind.indexOf("list") == 0)}
{#await question.getResponses(data.secret) then responses}
{#each responses as response (response.id)}
<div class="card mb-2">
<div class="card-body">
<p class="card-text" style:white-space="pre-line">
{response.value}
</p>
</div>
</div>
{/each}
{/await}
{:else}
<CorrectionPieChart {question} secret={data.secret} />
{/if}
<hr class="mb-3">
{/each}
{/await}
{/await}

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { user } from '../../stores/user';
import SurveyList from '../../components/SurveyList.svelte';
import { user } from '$lib/stores/user';
import SurveyList from '$lib/components/SurveyList.svelte';
</script>
<div class="card bg-light">

View file

@ -0,0 +1,9 @@
import { getSurvey } from '$lib/surveys';
export async function load({ params }) {
const survey = getSurvey(params.sid);
return {
survey,
};
}

View file

@ -1,27 +1,8 @@
<script context="module">
import { getSurvey } from '../../../lib/surveys';
export async function load({ params, stuff }) {
const survey = getSurvey(params.sid);
return {
props: {
survey,
},
stuff: {
...stuff,
survey,
}
};
}
</script>
<script lang="ts">
export let survey;
export let data;
</script>
{#await survey}
{#await data.survey}
<div class="text-center">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement du questionnaire &hellip;</span>

View file

@ -0,0 +1,5 @@
export async function load({ parent }) {
const stuff = await parent();
return stuff;
}

View file

@ -0,0 +1,60 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { user } from '$lib/stores/user';
import SurveyAdmin from '$lib/components/SurveyAdmin.svelte';
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import SurveyQuestions from '$lib/components/SurveyQuestions.svelte';
import { getQuestions } from '$lib/questions';
export let data;
let survey = null;
$: survey = data.survey;
$: if (survey.direct && !$user.is_admin) goto(`surveys/${survey.id}/live`);
let edit = false;
</script>
{#if $user && $user.is_admin}
<button class="btn btn-primary ms-1 float-end" on:click={() => { edit = !edit; } } title="Éditer"><i class="bi bi-pencil"></i></button>
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
{#if survey.direct}
<a href="surveys/{survey.id}/live" class="btn btn-danger ms-1 float-end" title="Aller au direct"><i class="bi bi-film"></i></a>
{/if}
{/if}
<div class="d-flex align-items-center">
<h2>
<a href="surveys/" class="text-muted" style="text-decoration: none">&lt;</a>
{survey.title}
</h2>
<SurveyBadge class="ms-2" {survey} />
</div>
{#if $user && $user.is_admin && edit}
<SurveyAdmin {survey} on:saved={() => edit = false} />
{/if}
{#await getQuestions(survey.id)}
<div class="text-center">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement des questions &hellip;</span>
</div>
{:then questions}
<SurveyQuestions {survey} {questions} />
{:catch error}
<div class="row mt-5">
<div class="d-none d-sm-block col-sm">
<hr>
</div>
<h3 class="col-sm-auto text-center text-muted mb-3"><label for="askquestion">Ce questionnaire n'est pas accessible</label></h3>
<div class="d-none d-sm-block col-sm">
<hr>
</div>
</div>
{#if survey.direct != null}
<div class="alert alert-warning">
<strong><a href="surveys/{survey.id}/live">Cliquez ici pour accéder au direct</a>.</strong> Il s'agit d'un questionnaire en direct, le questionnaire n'est pas accessible sur cette page.
</div>
{/if}
{/await}

View file

@ -1,704 +0,0 @@
<script context="module">
export async function load({ params, stuff }) {
return {
props: {
surveyP: stuff.survey,
sid: params.sid,
},
};
}
</script>
<script>
import { user } from '../../../stores/user';
import CorrectionPieChart from '../../../components/CorrectionPieChart.svelte';
import ListInputResponses from '../../../components/ListInputResponses.svelte';
import QuestionForm from '../../../components/QuestionForm.svelte';
import StartStopLiveSurvey from '../../../components/StartStopLiveSurvey.svelte';
import SurveyAdmin from '../../../components/SurveyAdmin.svelte';
import SurveyBadge from '../../../components/SurveyBadge.svelte';
import { getSurvey } from '../../../lib/surveys';
import { getQuestion, getQuestions, Question } from '../../../lib/questions';
import { getUsers } from '../../../lib/users';
export let surveyP;
export let sid;
let survey;
let req_questions;
surveyP.then((s) => {
survey = s;
updateQuestions();
if (survey.direct !== null) {
wsconnect();
}
});
function updateSurvey() {
surveyP = getSurvey(survey.id);
surveyP.then((s) => {
survey = s;
updateQuestions();
if (survey.direct !== null) {
wsconnect();
}
});
}
function updateQuestions() {
req_questions = getQuestions(survey.id);
}
function deleteQuestion(question) {
edit_question = null;
question.delete();
}
let ws = null;
let ws_up = false;
let wsstats = null;
let current_question = null;
let edit_question = null;
let responses = {};
let corrected = false;
let next_corrected = false;
let timer = 20;
let timer_end = null;
let timer_remain = 0;
let timer_cancel = null;
function updTimer() {
const now = new Date().getTime();
if (now > timer_end) {
timer_remain = 0;
clearInterval(timer_cancel);
timer_cancel = null;
} else {
timer_remain = Math.floor((timer_end - now) / 100)/10;
}
}
let users = {};
function updateUsers() {
getUsers().then((usr) => {
const tmp = { };
for (const u of usr) {
tmp[u.id.toString()] = u;
}
users = tmp;
});
}
updateUsers();
let scroll_states = { };
let scroll_mean = 0;
$: {
let mean = 0;
for (const k in scroll_states) {
mean += scroll_states[k];
}
scroll_mean = mean / Object.keys(scroll_states).length;
}
let responsesbyid = { };
$: {
const tmp = { };
for (const response in responses) {
if (!tmp[response]) tmp[response] = [];
for (const r in responses[response]) {
tmp[response].push(responses[response][r]);
}
}
responsesbyid = tmp;
}
let graph_data = {labels:[], datasets:[]};
async function reset_graph_data(questionid) {
if (questionid) {
const labels = [];
const flabels = [];
let question = null;
for (const q of await req_questions) {
if (q.id == current_question) {
question = q;
}
}
if (question) {
for (const p of await question.getProposals()) {
flabels.push(p.id.toString());
labels.push(p.label);
}
}
graph_data = {
labels,
flabels,
datasets: [
{
values: labels.map(() => 0)
}
]
}
}
if (current_question && responses[current_question] && graph_data.labels.length != 0) {
const values = graph_data.datasets[0].values.map(() => 0);
for (const u in responses[current_question]) {
const res = responses[current_question][u];
for (const r of res.split(',')) {
let idx = graph_data.flabels.indexOf(r);
values[idx] += 1;
}
}
graph_data.datasets[0].values = values;
}
}
let asks = [];
function wsconnect() {
if (ws !== null) return;
ws = new WebSocket((window.location.protocol == 'https:'?'wss://':'ws://') + window.location.host + `/api/surveys/${sid}/ws-admin`);
ws.addEventListener("open", () => {
ws_up = true;
ws.send('{"action":"get_responses"}');
ws.send('{"action":"get_stats"}');
ws.send('{"action":"get_asks"}');
});
ws.addEventListener("close", (e) => {
ws_up = false;
console.log('Socket is closed. Reconnect will be attempted in 1 second.');
setTimeout(function() {
ws = null;
updateSurvey();
}, 1500);
});
ws.addEventListener("error", (err) => {
ws_up = false;
console.log('Socket closed due to error.', err);
});
ws.addEventListener("message", (message) => {
const data = JSON.parse(message.data);
if (data.action && data.action == "new_question") {
current_question = data.question;
corrected = data.corrected == true;
if (timer_cancel) {
clearInterval(timer_cancel);
timer_cancel = null;
}
if (data.timer) {
timer_end = new Date().getTime() + data.timer;
timer_cancel = setInterval(updTimer, 250);
} else {
timer_end = null;
}
reset_graph_data(data.question);
} else if (data.action && data.action == "stats") {
wsstats = data.stats;
} else if (data.action && data.action == "new_response") {
if (!responses[data.question]) responses[data.question] = { };
responses[data.question][data.user] = data.value;
reset_graph_data();
} else if (data.action && data.action == "new_ask") {
asks.push({"id": data.question, "content": data.value, "userid": data.user});
asks = asks;
} else if (data.action && data.action == "myscroll" && wsstats && wsstats.users) {
scroll_states[data.user] = parseFloat(data.value);
for (const k in wsstats.users) {
if (wsstats.users[k].id == data.user) {
wsstats.users[k].myscroll = scroll_states[data.user];
}
}
} else if (data.action && data.action == "end") {
ws.close();
updateSurvey();
} else {
current_question = null;
timer_end = null;
if (timer_cancel) {
clearInterval(timer_cancel);
timer_cancel = null;
}
}
});
}
</script>
{#await surveyP then survey}
{#if $user && $user.is_admin}
<StartStopLiveSurvey
{survey}
class="ms-1 float-end"
on:update={() => updateSurvey()}
on:end={() => { if (confirm("Sûr ?")) ws.send('{"action":"end"}') }}
/>
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
{/if}
<div class="d-flex align-items-center">
<h2>
<a href="surveys/" class="text-muted" style="text-decoration: none">&lt;</a>
{survey.title}
<small class="text-muted">
Administration
</small>
{#if asks.length}
<a href="surveys/{sid}/admin#questions_part">
<i class="bi bi-patch-question-fill text-danger"></i>
</a>
{/if}
</h2>
{#if survey.direct !== null}
<div
class="badge rounded-pill ms-2"
class:bg-success={ws_up}
class:bg-danger={!ws_up}
>
{#if ws_up}Connecté{:else}Déconnecté{/if}
</div>
{:else}
<SurveyBadge
class="mx-2"
{survey}
/>
{/if}
</div>
{#if survey.direct === null}
<SurveyAdmin
{survey}
on:saved={updateSurvey}
/>
{:else}
{#await req_questions}
<div class="text-center">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement des questions &hellip;</span>
</div>
{:then questions}
<div class="card my-3">
<table class="table table-hover table-striped mb-0">
<thead>
<tr>
<th>
Question
{#if timer_end}
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
<input
type="number"
class="form-control"
disabled
value={timer_remain}
>
<span class="input-group-text">s</span>
</div>
{:else}
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
<input
type="number"
class="form-control"
bind:value={timer}
placeholder="Valeur du timer"
>
<span class="input-group-text">s</span>
</div>
{/if}
<button
type="button"
class="btn btn-sm btn-info ms-1"
on:click={updateQuestions}
title="Rafraîchir les questions"
>
<i class="bi bi-arrow-counterclockwise"></i>
</button>
</th>
<th>
Réponses
</th>
<th>
Actions
<button
type="button"
class="btn btn-sm btn-primary"
disabled={!current_question || !ws_up}
on:click={() => { ws.send('{"action":"pause"}')} }
title="Passer sur une scène sans question"
>
<i class="bi bi-pause-fill"></i>
</button>
<button
type="button"
class="btn btn-sm"
class:btn-outline-success={!next_corrected}
class:btn-success={next_corrected}
on:click={() => { next_corrected = !next_corrected } }
title="La prochaine question est affichée corrigée"
>
<i class="bi bi-eye"></i>
</button>
<button
type="button"
class="btn btn-sm btn-info"
on:click={() => { edit_question = new Question({ id_survey: survey.id }) } }
title="Ajouter une question"
>
<i class="bi bi-plus"></i>
</button>
<button
type="button"
class="btn btn-sm btn-outline-danger"
on:click={() => { fetch('api/cache', {method: 'DELETE'}) } }
title="Vider les caches"
>
<i class="bi bi-bandaid-fill"></i>
</button>
</th>
</tr>
</thead>
<tbody>
{#each questions as question (question.id)}
<tr>
<td>
{#if responses[question.id]}
<a href="surveys/{sid}/admin#q{question.id}_res">
{question.title}
</a>
{:else}
{question.title}
{/if}
</td>
<td>
{#if responses[question.id]}
{Object.keys(responses[question.id]).length}
{:else}
0
{/if}
{#if wsstats}/ {wsstats.nb_clients}{/if}
</td>
<td>
<button
type="button"
class="btn btn-sm"
class:btn-primary={!next_corrected}
class:btn-success={next_corrected}
disabled={(question.id === current_question && next_corrected == corrected) || !ws_up}
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": 0, "question":' + question.id + '}')} }
>
<i class="bi bi-play-fill"></i>
</button>
<button
type="button"
class="btn btn-sm btn-danger"
disabled={question.id === current_question || !ws_up}
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": ' + timer * 1000 + ',"question":' + question.id + '}')} }
>
<i class="bi bi-stopwatch-fill"></i>
</button>
<a
href="/surveys/{survey.id}/responses/{question.id}"
target="_blank"
type="button"
class="btn btn-sm btn-success"
>
<i class="bi bi-files"></i>
</a>
<button
type="button"
class="btn btn-sm btn-info"
disabled={question.id === current_question}
on:click={() => { getQuestion(question.id).then((q) => {edit_question = q})} }
>
<i class="bi bi-pencil"></i>
</button>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{/await}
{#if edit_question !== null}
<QuestionForm
{survey}
edit
question={edit_question}
on:delete={() => deleteQuestion(edit_question)}
/>
{/if}
<hr>
<button
type="button"
class="btn btn-sm btn-info ms-1 float-end"
on:click={() => { ws.send('{"action":"get_asks", "value": ""}'); asks = []; }}
title="Rafraîchir les réponses"
>
<i class="bi bi-arrow-counterclockwise"></i>
<i class="bi bi-question-diamond"></i>
</button>
<button
type="button"
class="btn btn-sm btn-light ms-1 float-end"
on:click={() => { ws.send('{"action":"get_asks", "value": "unanswered"}'); asks = []; }}
title="Rafraîchir les réponses, en rapportant les réponses déjà répondues"
>
<i class="bi bi-arrow-counterclockwise"></i>
<i class="bi bi-question-diamond"></i>
</button>
<button
type="button"
class="btn btn-sm btn-success float-end"
title="Tout marqué comme répondu"
on:click={() => { ws.send('{"action":"mark_answered", "value": "all"}'); asks = [] }}
>
<i class="bi bi-check-all"></i>
</button>
<h3 id="questions_part">
Questions
{#if asks.length}
<small class="text-muted">
{asks.length}&nbsp;question{#if asks.length > 1}s{/if}
</small>
{/if}
</h3>
{#if asks.length}
{#each asks as ask (ask.id)}
<div class="card mb-3">
<div class="card-body">
<p class="card-text">
{ask.content}
</p>
</div>
<div class="card-footer">
<button
type="button"
class="btn btn-sm btn-success float-end"
title="Marqué comme répondu"
on:click={() => { ws.send('{"action":"mark_answered", "question": ' + ask.id + '}'); asks = asks.filter((e) => e.id != ask.id) }}
>
<i class="bi bi-check"></i>
</button>
Par
<a href="users/{ask.userid}" target="_blank">
{#if users && users[ask.userid]}
{users[ask.userid].login}
{:else}
{ask.userid}
{/if}
</a>
</div>
</div>
{/each}
{:else}
<div class="text-center text-muted">
Pas de question pour l'instant.
</div>
{/if}
<hr>
<button
type="button"
class="btn btn-sm btn-info ms-1 float-end"
on:click={() => { ws.send('{"action":"get_responses"}') }}
title="Rafraîchir les réponses"
>
<i class="bi bi-arrow-counterclockwise"></i>
<i class="bi bi-card-checklist"></i>
</button>
<h3>
Réponses
</h3>
{#if Object.keys(responses).length}
{#each Object.keys(responses) as q, qid (qid)}
{#await req_questions then questions}
{#each questions as question}
{#if question.id == q}
<h4 id="q{question.id}_res">
{question.title}
</h4>
{#if question.kind == 'ucq'}
{#await question.getProposals()}
<div class="text-center">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement des propositions &hellip;</span>
</div>
{:then proposals}
{#if current_question == question.id}
<CorrectionPieChart
{question}
{proposals}
data={graph_data}
/>
{:else}
<CorrectionPieChart
{question}
/>
{/if}
<div class="card mb-4">
<table class="table table-sm table-striped table-hover mb-0">
<tbody>
{#each proposals as proposal (proposal.id)}
<tr>
<td>
{proposal.label}
</td>
<td>
{responsesbyid[q].filter((e) => e == proposal.id.toString()).length}/{responsesbyid[q].length}
</td>
<td>
{Math.trunc(responsesbyid[q].filter((e) => e == proposal.id.toString()).length / responsesbyid[q].length * 1000)/10}&nbsp;%
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{/await}
{:else if question.kind == 'mcq'}
{#await question.getProposals()}
<div class="text-center">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement des propositions &hellip;</span>
</div>
{:then proposals}
{#if current_question == question.id}
<CorrectionPieChart
{question}
{proposals}
data={graph_data}
/>
{:else}
<CorrectionPieChart
{question}
/>
{/if}
<div class="card mb-4">
<table class="table table-sm table-striped table-hover mb-0">
<tbody>
{#each proposals as proposal (proposal.id)}
<tr>
<td>
{proposal.label}
</td>
<td>
{responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length}/{responsesbyid[q].length}
</td>
<td>
{Math.trunc(responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length / responsesbyid[q].length * 1000)/10}&nbsp;%
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{/await}
{:else if question.kind && question.kind.startsWith('list')}
<ListInputResponses
responses={responses[q]}
{users}
/>
{:else}
<div class="card mb-4">
<ul class="list-group list-group-flush">
{#each Object.keys(responses[q]) as user, rid (rid)}
<li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
<span>
{responses[q][user]}
</span>
<a href="users/{user}" target="_blank" class="badge bg-dark rounded-pill">
{#if users && users[user]}
{users[user].login}
{:else}
{user}
{/if}
</a>
</li>
{/each}
</ul>
</div>
{/if}
{/if}
{/each}
{/await}
{/each}
{/if}
<hr>
<button
type="button"
class="btn btn-sm btn-info ms-1 float-end"
on:click={() => { ws.send('{"action":"get_stats"}') }}
title="Rafraîchir les stats"
>
<i class="bi bi-arrow-counterclockwise"></i>
<i class="bi bi-123"></i>
</button>
<button
type="button"
class="btn btn-sm btn-primary ms-1 float-end"
title="Rafraîchir la liste des utilisateurs"
on:click={updateUsers}
>
<i class="bi bi-arrow-clockwise"></i>
<i class="bi bi-people"></i>
</button>
<button
type="button"
class="btn btn-sm btn-warning ms-1 float-end"
on:click={() => { scroll_states = {}; ws.send('{"action":"where_are_you"}')} }
title="Rapporter l'avancement"
>
<i class="bi bi-geo-fill"></i>
</button>
<h3 id="users">
Connectés
{#if wsstats}
<small class="text-muted">{wsstats.nb_clients} utilisateurs</small>
{/if}
{#if scroll_mean}
<small
class:text-danger={scroll_mean >= 0 && scroll_mean < 0.2}
class:text-warning={scroll_mean >= 0.2 && scroll_mean < 0.6}
class:text-info={scroll_mean >= 0.6 && scroll_mean < 0.9}
class:text-success={scroll_mean >= 0.9}
>Avancement global&nbsp;: {Math.trunc(scroll_mean*10000)/100} %</small>
{/if}
</h3>
{#if wsstats && wsstats.users}
<div class="row row-cols-5 py-3">
{#each wsstats.users as user, lid (lid)}
<div class="col">
<div class="card">
<img alt="{user.login}" src="//photos.cri.epita.fr/thumb/{user.login}" class="card-img-top">
<div class="card-footer text-center text-truncate p-0">
<a href="users/{user.login}" target="_blank">
{user.login}
</a>
</div>
{#if user.myscroll != null}
<div
class="card-footer py-0 px-1"
class:bg-danger={user.myscroll >= 0 && user.myscroll < 0.2}
class:bg-warning={user.myscroll >= 0.2 && user.myscroll < 0.6}
class:bg-info={user.myscroll >= 0.6 && user.myscroll < 0.9}
class:bg-success={user.myscroll >= 0.9}
>
Avancement&nbsp;: {Math.trunc(user.myscroll*10000)/100}&nbsp;%
</div>
{/if}
</div>
</div>
{/each}
</div>
{/if}
{/if}
{/await}

View file

@ -0,0 +1,8 @@
export async function load({ parent, params }) {
const stuff = await parent();
return {
survey: stuff.survey,
sid: params.sid,
};
}

Some files were not shown because too many files have changed in this diff Show more