diff --git a/.run/nwerc-2018.run.xml b/.run/nwerc-2018.run.xml new file mode 100644 index 000000000..af2653781 --- /dev/null +++ b/.run/nwerc-2018.run.xml @@ -0,0 +1,24 @@ + + + + + + + true + true + false + false + + + \ No newline at end of file diff --git a/config/_examples/_domjudge_test1/settings.json b/config/_examples/_domjudge_test1/settings.json index 2284fb75b..0bc87cab4 100644 --- a/config/_examples/_domjudge_test1/settings.json +++ b/config/_examples/_domjudge_test1/settings.json @@ -1,6 +1,11 @@ -{ - "login":"live", - "password":"$creds.cds_password_pretest_dhaka", - "url":"https://cds.storm.vu/api/contests/pretest_dhaka", - "type":"clics" +{ + "type":"clics", + "feeds": [ + { + "login":"live", + "password":"$creds.cds_password_pretest_dhaka", + "url":"https://cds.storm.vu/api", + "contestId": "pretest_dhaka" + } + ] } \ No newline at end of file diff --git a/config/_examples/_pc2/settings.json b/config/_examples/_pc2/settings.json index f912fab59..b2629e76c 100644 --- a/config/_examples/_pc2/settings.json +++ b/config/_examples/_pc2/settings.json @@ -1,8 +1,13 @@ { "type":"clics", - - "url":"_examples/_pc2", - "feedVersion":"2020_03", + "feeds": [ + { + "url": ".", + "contestId": "", + "eventFeedPath": "", + "feedVersion": "2020_03" + } + ], "emulation":{"speed":5,"startTime":"now"} } diff --git a/config/ejoi/2023/advanced.json b/config/ejoi/2023/advanced.json new file mode 100644 index 000000000..1d22236b2 --- /dev/null +++ b/config/ejoi/2023/advanced.json @@ -0,0 +1,58 @@ +{ + "freezeTimeSeconds": 18000, + "teamOverrides": { + "GEO5" : {"isOutOfContest": true }, + "GEO6" : {"isOutOfContest": true }, + "GEO7" : {"isOutOfContest": true }, + "GEO8" : {"isOutOfContest": true } + }, + "teamOverrideTemplate": { + "displayName": "[{country}] {first_name} {last_name}" + }, + "teamMediaTemplate": { + "photo": { + "type": "Photo", + "url": "/media/photos/{teamId}.png" + } + }, + "problemOverrides": { + "search": { + "displayName": "A1" + }, + "teleport": { + "displayName": "B1" + }, + "offices": { + "displayName": "C1" + }, + "puzzle": { + "displayName": "A2" + }, + "infection": { + "displayName": "B2" + }, + "team": { + "displayName": "C2" + } + }, + "scoreboardOverrides": { + "medals": [ + { + "name": "gold", + "count": 8, + "tiebreakMode": "ALL" + }, + { + "name": "silver", + "count": 15, + "tiebreakMode": "ALL" + }, + { + "name": "bronze", + "count": 23, + "tiebreakMode": "ALL" + } + ] + } + +} \ No newline at end of file diff --git a/config/ejoi/2023/media/photos/ARM1.png b/config/ejoi/2023/media/photos/ARM1.png new file mode 100644 index 000000000..84958fb41 Binary files /dev/null and b/config/ejoi/2023/media/photos/ARM1.png differ diff --git a/config/ejoi/2023/media/photos/ARM2.png b/config/ejoi/2023/media/photos/ARM2.png new file mode 100644 index 000000000..a74513ded Binary files /dev/null and b/config/ejoi/2023/media/photos/ARM2.png differ diff --git a/config/ejoi/2023/media/photos/ARM3.png b/config/ejoi/2023/media/photos/ARM3.png new file mode 100644 index 000000000..62812fc93 Binary files /dev/null and b/config/ejoi/2023/media/photos/ARM3.png differ diff --git a/config/ejoi/2023/media/photos/ARM4.png b/config/ejoi/2023/media/photos/ARM4.png new file mode 100644 index 000000000..ca257001d Binary files /dev/null and b/config/ejoi/2023/media/photos/ARM4.png differ diff --git a/config/ejoi/2023/media/photos/AZE1.png b/config/ejoi/2023/media/photos/AZE1.png new file mode 100644 index 000000000..2ea35d595 Binary files /dev/null and b/config/ejoi/2023/media/photos/AZE1.png differ diff --git a/config/ejoi/2023/media/photos/AZE2.png b/config/ejoi/2023/media/photos/AZE2.png new file mode 100644 index 000000000..00dea837e Binary files /dev/null and b/config/ejoi/2023/media/photos/AZE2.png differ diff --git a/config/ejoi/2023/media/photos/AZE3.png b/config/ejoi/2023/media/photos/AZE3.png new file mode 100644 index 000000000..c3550f469 Binary files /dev/null and b/config/ejoi/2023/media/photos/AZE3.png differ diff --git a/config/ejoi/2023/media/photos/AZE4.png b/config/ejoi/2023/media/photos/AZE4.png new file mode 100644 index 000000000..6b601bc15 Binary files /dev/null and b/config/ejoi/2023/media/photos/AZE4.png differ diff --git a/config/ejoi/2023/media/photos/BEL1.png b/config/ejoi/2023/media/photos/BEL1.png new file mode 100644 index 000000000..6f45174cd Binary files /dev/null and b/config/ejoi/2023/media/photos/BEL1.png differ diff --git a/config/ejoi/2023/media/photos/BEL2.png b/config/ejoi/2023/media/photos/BEL2.png new file mode 100644 index 000000000..6dc507af2 Binary files /dev/null and b/config/ejoi/2023/media/photos/BEL2.png differ diff --git a/config/ejoi/2023/media/photos/BEL3.png b/config/ejoi/2023/media/photos/BEL3.png new file mode 100644 index 000000000..9d9a2bad4 Binary files /dev/null and b/config/ejoi/2023/media/photos/BEL3.png differ diff --git a/config/ejoi/2023/media/photos/BEL4.png b/config/ejoi/2023/media/photos/BEL4.png new file mode 100644 index 000000000..81ea79f20 Binary files /dev/null and b/config/ejoi/2023/media/photos/BEL4.png differ diff --git a/config/ejoi/2023/media/photos/BGR1.png b/config/ejoi/2023/media/photos/BGR1.png new file mode 100644 index 000000000..00a44b96d Binary files /dev/null and b/config/ejoi/2023/media/photos/BGR1.png differ diff --git a/config/ejoi/2023/media/photos/BGR2.png b/config/ejoi/2023/media/photos/BGR2.png new file mode 100644 index 000000000..8b6aa5126 Binary files /dev/null and b/config/ejoi/2023/media/photos/BGR2.png differ diff --git a/config/ejoi/2023/media/photos/BGR3.png b/config/ejoi/2023/media/photos/BGR3.png new file mode 100644 index 000000000..307595053 Binary files /dev/null and b/config/ejoi/2023/media/photos/BGR3.png differ diff --git a/config/ejoi/2023/media/photos/BGR4.png b/config/ejoi/2023/media/photos/BGR4.png new file mode 100644 index 000000000..dc8640ae6 Binary files /dev/null and b/config/ejoi/2023/media/photos/BGR4.png differ diff --git a/config/ejoi/2023/media/photos/BIH1.png b/config/ejoi/2023/media/photos/BIH1.png new file mode 100644 index 000000000..cb32a252f Binary files /dev/null and b/config/ejoi/2023/media/photos/BIH1.png differ diff --git a/config/ejoi/2023/media/photos/BIH2.png b/config/ejoi/2023/media/photos/BIH2.png new file mode 100644 index 000000000..4f33cad2c Binary files /dev/null and b/config/ejoi/2023/media/photos/BIH2.png differ diff --git a/config/ejoi/2023/media/photos/BIH3.png b/config/ejoi/2023/media/photos/BIH3.png new file mode 100644 index 000000000..0f71714b7 Binary files /dev/null and b/config/ejoi/2023/media/photos/BIH3.png differ diff --git a/config/ejoi/2023/media/photos/BIH4.png b/config/ejoi/2023/media/photos/BIH4.png new file mode 100644 index 000000000..4be0f93e5 Binary files /dev/null and b/config/ejoi/2023/media/photos/BIH4.png differ diff --git a/config/ejoi/2023/media/photos/CHE1.png b/config/ejoi/2023/media/photos/CHE1.png new file mode 100644 index 000000000..7c4fca5b6 Binary files /dev/null and b/config/ejoi/2023/media/photos/CHE1.png differ diff --git a/config/ejoi/2023/media/photos/CHE2.png b/config/ejoi/2023/media/photos/CHE2.png new file mode 100644 index 000000000..30e621d67 Binary files /dev/null and b/config/ejoi/2023/media/photos/CHE2.png differ diff --git a/config/ejoi/2023/media/photos/CHE3.png b/config/ejoi/2023/media/photos/CHE3.png new file mode 100644 index 000000000..da4e60065 Binary files /dev/null and b/config/ejoi/2023/media/photos/CHE3.png differ diff --git a/config/ejoi/2023/media/photos/CYP1.png b/config/ejoi/2023/media/photos/CYP1.png new file mode 100644 index 000000000..c9d1c5b3d Binary files /dev/null and b/config/ejoi/2023/media/photos/CYP1.png differ diff --git a/config/ejoi/2023/media/photos/CYP2.png b/config/ejoi/2023/media/photos/CYP2.png new file mode 100644 index 000000000..e0b283a48 Binary files /dev/null and b/config/ejoi/2023/media/photos/CYP2.png differ diff --git a/config/ejoi/2023/media/photos/CYP3.png b/config/ejoi/2023/media/photos/CYP3.png new file mode 100644 index 000000000..8ab7d3b73 Binary files /dev/null and b/config/ejoi/2023/media/photos/CYP3.png differ diff --git a/config/ejoi/2023/media/photos/CYP4.png b/config/ejoi/2023/media/photos/CYP4.png new file mode 100644 index 000000000..85cbc3878 Binary files /dev/null and b/config/ejoi/2023/media/photos/CYP4.png differ diff --git a/config/ejoi/2023/media/photos/EST1.png b/config/ejoi/2023/media/photos/EST1.png new file mode 100644 index 000000000..e1e7eb319 Binary files /dev/null and b/config/ejoi/2023/media/photos/EST1.png differ diff --git a/config/ejoi/2023/media/photos/EST2.png b/config/ejoi/2023/media/photos/EST2.png new file mode 100644 index 000000000..3a57a9783 Binary files /dev/null and b/config/ejoi/2023/media/photos/EST2.png differ diff --git a/config/ejoi/2023/media/photos/FRA1.png b/config/ejoi/2023/media/photos/FRA1.png new file mode 100644 index 000000000..39b771a00 Binary files /dev/null and b/config/ejoi/2023/media/photos/FRA1.png differ diff --git a/config/ejoi/2023/media/photos/FRA2.png b/config/ejoi/2023/media/photos/FRA2.png new file mode 100644 index 000000000..5f896e507 Binary files /dev/null and b/config/ejoi/2023/media/photos/FRA2.png differ diff --git a/config/ejoi/2023/media/photos/FRA3.png b/config/ejoi/2023/media/photos/FRA3.png new file mode 100644 index 000000000..099b45508 Binary files /dev/null and b/config/ejoi/2023/media/photos/FRA3.png differ diff --git a/config/ejoi/2023/media/photos/FRA4.png b/config/ejoi/2023/media/photos/FRA4.png new file mode 100644 index 000000000..6324fffd7 Binary files /dev/null and b/config/ejoi/2023/media/photos/FRA4.png differ diff --git a/config/ejoi/2023/media/photos/GEO1.png b/config/ejoi/2023/media/photos/GEO1.png new file mode 100644 index 000000000..71f38f6e3 Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO1.png differ diff --git a/config/ejoi/2023/media/photos/GEO2.png b/config/ejoi/2023/media/photos/GEO2.png new file mode 100644 index 000000000..b43b9b528 Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO2.png differ diff --git a/config/ejoi/2023/media/photos/GEO3.png b/config/ejoi/2023/media/photos/GEO3.png new file mode 100644 index 000000000..42c675e33 Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO3.png differ diff --git a/config/ejoi/2023/media/photos/GEO4.png b/config/ejoi/2023/media/photos/GEO4.png new file mode 100644 index 000000000..19ae85a51 Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO4.png differ diff --git a/config/ejoi/2023/media/photos/GEO5.png b/config/ejoi/2023/media/photos/GEO5.png new file mode 100644 index 000000000..7c3689913 Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO5.png differ diff --git a/config/ejoi/2023/media/photos/GEO6.png b/config/ejoi/2023/media/photos/GEO6.png new file mode 100644 index 000000000..843ea5a9e Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO6.png differ diff --git a/config/ejoi/2023/media/photos/GEO7.png b/config/ejoi/2023/media/photos/GEO7.png new file mode 100644 index 000000000..fb6ed2564 Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO7.png differ diff --git a/config/ejoi/2023/media/photos/GEO8.png b/config/ejoi/2023/media/photos/GEO8.png new file mode 100644 index 000000000..fce044d69 Binary files /dev/null and b/config/ejoi/2023/media/photos/GEO8.png differ diff --git a/config/ejoi/2023/media/photos/GRC1.png b/config/ejoi/2023/media/photos/GRC1.png new file mode 100644 index 000000000..5ada6c172 Binary files /dev/null and b/config/ejoi/2023/media/photos/GRC1.png differ diff --git a/config/ejoi/2023/media/photos/GRC2.png b/config/ejoi/2023/media/photos/GRC2.png new file mode 100644 index 000000000..50b458be0 Binary files /dev/null and b/config/ejoi/2023/media/photos/GRC2.png differ diff --git a/config/ejoi/2023/media/photos/GRC3.png b/config/ejoi/2023/media/photos/GRC3.png new file mode 100644 index 000000000..b995c07ea Binary files /dev/null and b/config/ejoi/2023/media/photos/GRC3.png differ diff --git a/config/ejoi/2023/media/photos/GRC4.png b/config/ejoi/2023/media/photos/GRC4.png new file mode 100644 index 000000000..f892720fd Binary files /dev/null and b/config/ejoi/2023/media/photos/GRC4.png differ diff --git a/config/ejoi/2023/media/photos/HRV1.png b/config/ejoi/2023/media/photos/HRV1.png new file mode 100644 index 000000000..03c161501 Binary files /dev/null and b/config/ejoi/2023/media/photos/HRV1.png differ diff --git a/config/ejoi/2023/media/photos/HRV2.png b/config/ejoi/2023/media/photos/HRV2.png new file mode 100644 index 000000000..3b06049ac Binary files /dev/null and b/config/ejoi/2023/media/photos/HRV2.png differ diff --git a/config/ejoi/2023/media/photos/HRV3.png b/config/ejoi/2023/media/photos/HRV3.png new file mode 100644 index 000000000..e213ac621 Binary files /dev/null and b/config/ejoi/2023/media/photos/HRV3.png differ diff --git a/config/ejoi/2023/media/photos/HRV4.png b/config/ejoi/2023/media/photos/HRV4.png new file mode 100644 index 000000000..6752c9965 Binary files /dev/null and b/config/ejoi/2023/media/photos/HRV4.png differ diff --git a/config/ejoi/2023/media/photos/HUN1.png b/config/ejoi/2023/media/photos/HUN1.png new file mode 100644 index 000000000..07d4b7fb7 Binary files /dev/null and b/config/ejoi/2023/media/photos/HUN1.png differ diff --git a/config/ejoi/2023/media/photos/HUN2.png b/config/ejoi/2023/media/photos/HUN2.png new file mode 100644 index 000000000..932f794c4 Binary files /dev/null and b/config/ejoi/2023/media/photos/HUN2.png differ diff --git a/config/ejoi/2023/media/photos/HUN3.png b/config/ejoi/2023/media/photos/HUN3.png new file mode 100644 index 000000000..b2e5fda95 Binary files /dev/null and b/config/ejoi/2023/media/photos/HUN3.png differ diff --git a/config/ejoi/2023/media/photos/HUN4.png b/config/ejoi/2023/media/photos/HUN4.png new file mode 100644 index 000000000..7ea0ff37a Binary files /dev/null and b/config/ejoi/2023/media/photos/HUN4.png differ diff --git a/config/ejoi/2023/media/photos/KAZ1.png b/config/ejoi/2023/media/photos/KAZ1.png new file mode 100644 index 000000000..ea1cf17d2 Binary files /dev/null and b/config/ejoi/2023/media/photos/KAZ1.png differ diff --git a/config/ejoi/2023/media/photos/KAZ2.png b/config/ejoi/2023/media/photos/KAZ2.png new file mode 100644 index 000000000..fda334049 Binary files /dev/null and b/config/ejoi/2023/media/photos/KAZ2.png differ diff --git a/config/ejoi/2023/media/photos/KAZ3.png b/config/ejoi/2023/media/photos/KAZ3.png new file mode 100644 index 000000000..b4314610d Binary files /dev/null and b/config/ejoi/2023/media/photos/KAZ3.png differ diff --git a/config/ejoi/2023/media/photos/KAZ4.png b/config/ejoi/2023/media/photos/KAZ4.png new file mode 100644 index 000000000..8ffc9b027 Binary files /dev/null and b/config/ejoi/2023/media/photos/KAZ4.png differ diff --git a/config/ejoi/2023/media/photos/LTU1.png b/config/ejoi/2023/media/photos/LTU1.png new file mode 100644 index 000000000..fe7f7deca Binary files /dev/null and b/config/ejoi/2023/media/photos/LTU1.png differ diff --git a/config/ejoi/2023/media/photos/LTU2.png b/config/ejoi/2023/media/photos/LTU2.png new file mode 100644 index 000000000..d57f8f44d Binary files /dev/null and b/config/ejoi/2023/media/photos/LTU2.png differ diff --git a/config/ejoi/2023/media/photos/LTU3.png b/config/ejoi/2023/media/photos/LTU3.png new file mode 100644 index 000000000..cebda2222 Binary files /dev/null and b/config/ejoi/2023/media/photos/LTU3.png differ diff --git a/config/ejoi/2023/media/photos/LTU4.png b/config/ejoi/2023/media/photos/LTU4.png new file mode 100644 index 000000000..7c9a963c0 Binary files /dev/null and b/config/ejoi/2023/media/photos/LTU4.png differ diff --git a/config/ejoi/2023/media/photos/LVA1.png b/config/ejoi/2023/media/photos/LVA1.png new file mode 100644 index 000000000..798d4570b Binary files /dev/null and b/config/ejoi/2023/media/photos/LVA1.png differ diff --git a/config/ejoi/2023/media/photos/LVA2.png b/config/ejoi/2023/media/photos/LVA2.png new file mode 100644 index 000000000..6f9490582 Binary files /dev/null and b/config/ejoi/2023/media/photos/LVA2.png differ diff --git a/config/ejoi/2023/media/photos/LVA3.png b/config/ejoi/2023/media/photos/LVA3.png new file mode 100644 index 000000000..b6e739f55 Binary files /dev/null and b/config/ejoi/2023/media/photos/LVA3.png differ diff --git a/config/ejoi/2023/media/photos/LVA4.png b/config/ejoi/2023/media/photos/LVA4.png new file mode 100644 index 000000000..ab894689a Binary files /dev/null and b/config/ejoi/2023/media/photos/LVA4.png differ diff --git a/config/ejoi/2023/media/photos/MDA1.png b/config/ejoi/2023/media/photos/MDA1.png new file mode 100644 index 000000000..f94ab6491 Binary files /dev/null and b/config/ejoi/2023/media/photos/MDA1.png differ diff --git a/config/ejoi/2023/media/photos/MDA2.png b/config/ejoi/2023/media/photos/MDA2.png new file mode 100644 index 000000000..d77f6e1ac Binary files /dev/null and b/config/ejoi/2023/media/photos/MDA2.png differ diff --git a/config/ejoi/2023/media/photos/MDA3.png b/config/ejoi/2023/media/photos/MDA3.png new file mode 100644 index 000000000..b2aec9cd6 Binary files /dev/null and b/config/ejoi/2023/media/photos/MDA3.png differ diff --git a/config/ejoi/2023/media/photos/MKD1.png b/config/ejoi/2023/media/photos/MKD1.png new file mode 100644 index 000000000..2ca23bec2 Binary files /dev/null and b/config/ejoi/2023/media/photos/MKD1.png differ diff --git a/config/ejoi/2023/media/photos/MKD2.png b/config/ejoi/2023/media/photos/MKD2.png new file mode 100644 index 000000000..e2ab7a620 Binary files /dev/null and b/config/ejoi/2023/media/photos/MKD2.png differ diff --git a/config/ejoi/2023/media/photos/MKD3.png b/config/ejoi/2023/media/photos/MKD3.png new file mode 100644 index 000000000..51a14e6be Binary files /dev/null and b/config/ejoi/2023/media/photos/MKD3.png differ diff --git a/config/ejoi/2023/media/photos/MKD4.png b/config/ejoi/2023/media/photos/MKD4.png new file mode 100644 index 000000000..188de2604 Binary files /dev/null and b/config/ejoi/2023/media/photos/MKD4.png differ diff --git a/config/ejoi/2023/media/photos/POL1.png b/config/ejoi/2023/media/photos/POL1.png new file mode 100644 index 000000000..6d9d45b36 Binary files /dev/null and b/config/ejoi/2023/media/photos/POL1.png differ diff --git a/config/ejoi/2023/media/photos/POL2.png b/config/ejoi/2023/media/photos/POL2.png new file mode 100644 index 000000000..568f9e30c Binary files /dev/null and b/config/ejoi/2023/media/photos/POL2.png differ diff --git a/config/ejoi/2023/media/photos/POL3.png b/config/ejoi/2023/media/photos/POL3.png new file mode 100644 index 000000000..bfdb3815a Binary files /dev/null and b/config/ejoi/2023/media/photos/POL3.png differ diff --git a/config/ejoi/2023/media/photos/POL4.png b/config/ejoi/2023/media/photos/POL4.png new file mode 100644 index 000000000..43b6f94a3 Binary files /dev/null and b/config/ejoi/2023/media/photos/POL4.png differ diff --git a/config/ejoi/2023/media/photos/ROU1.png b/config/ejoi/2023/media/photos/ROU1.png new file mode 100644 index 000000000..43c1f1c4a Binary files /dev/null and b/config/ejoi/2023/media/photos/ROU1.png differ diff --git a/config/ejoi/2023/media/photos/ROU2.png b/config/ejoi/2023/media/photos/ROU2.png new file mode 100644 index 000000000..3c5a83929 Binary files /dev/null and b/config/ejoi/2023/media/photos/ROU2.png differ diff --git a/config/ejoi/2023/media/photos/ROU3.png b/config/ejoi/2023/media/photos/ROU3.png new file mode 100644 index 000000000..7cc4939b5 Binary files /dev/null and b/config/ejoi/2023/media/photos/ROU3.png differ diff --git a/config/ejoi/2023/media/photos/ROU4.png b/config/ejoi/2023/media/photos/ROU4.png new file mode 100644 index 000000000..914ec5cc0 Binary files /dev/null and b/config/ejoi/2023/media/photos/ROU4.png differ diff --git a/config/ejoi/2023/media/photos/SRB1.png b/config/ejoi/2023/media/photos/SRB1.png new file mode 100644 index 000000000..d41fe0ac7 Binary files /dev/null and b/config/ejoi/2023/media/photos/SRB1.png differ diff --git a/config/ejoi/2023/media/photos/SRB2.png b/config/ejoi/2023/media/photos/SRB2.png new file mode 100644 index 000000000..edb4c763f Binary files /dev/null and b/config/ejoi/2023/media/photos/SRB2.png differ diff --git a/config/ejoi/2023/media/photos/SRB3.png b/config/ejoi/2023/media/photos/SRB3.png new file mode 100644 index 000000000..6b5cac75c Binary files /dev/null and b/config/ejoi/2023/media/photos/SRB3.png differ diff --git a/config/ejoi/2023/media/photos/SRB4.png b/config/ejoi/2023/media/photos/SRB4.png new file mode 100644 index 000000000..002f4747e Binary files /dev/null and b/config/ejoi/2023/media/photos/SRB4.png differ diff --git a/config/ejoi/2023/media/photos/SVN1.png b/config/ejoi/2023/media/photos/SVN1.png new file mode 100644 index 000000000..d57a47d92 Binary files /dev/null and b/config/ejoi/2023/media/photos/SVN1.png differ diff --git a/config/ejoi/2023/media/photos/SVN2.png b/config/ejoi/2023/media/photos/SVN2.png new file mode 100644 index 000000000..39c083d66 Binary files /dev/null and b/config/ejoi/2023/media/photos/SVN2.png differ diff --git a/config/ejoi/2023/media/photos/SVN3.png b/config/ejoi/2023/media/photos/SVN3.png new file mode 100644 index 000000000..5504a8b70 Binary files /dev/null and b/config/ejoi/2023/media/photos/SVN3.png differ diff --git a/config/ejoi/2023/media/photos/SVN4.png b/config/ejoi/2023/media/photos/SVN4.png new file mode 100644 index 000000000..8cd511c1c Binary files /dev/null and b/config/ejoi/2023/media/photos/SVN4.png differ diff --git a/config/ejoi/2023/media/photos/TUR1.png b/config/ejoi/2023/media/photos/TUR1.png new file mode 100644 index 000000000..f56c7d1ce Binary files /dev/null and b/config/ejoi/2023/media/photos/TUR1.png differ diff --git a/config/ejoi/2023/media/photos/TUR2.png b/config/ejoi/2023/media/photos/TUR2.png new file mode 100644 index 000000000..df7a0f8ae Binary files /dev/null and b/config/ejoi/2023/media/photos/TUR2.png differ diff --git a/config/ejoi/2023/media/photos/TUR3.png b/config/ejoi/2023/media/photos/TUR3.png new file mode 100644 index 000000000..4690cc15b Binary files /dev/null and b/config/ejoi/2023/media/photos/TUR3.png differ diff --git a/config/ejoi/2023/media/photos/TUR4.png b/config/ejoi/2023/media/photos/TUR4.png new file mode 100644 index 000000000..f0026c77a Binary files /dev/null and b/config/ejoi/2023/media/photos/TUR4.png differ diff --git a/config/ejoi/2023/media/photos/UKR1.png b/config/ejoi/2023/media/photos/UKR1.png new file mode 100644 index 000000000..096bfdea8 Binary files /dev/null and b/config/ejoi/2023/media/photos/UKR1.png differ diff --git a/config/ejoi/2023/media/photos/UKR2.png b/config/ejoi/2023/media/photos/UKR2.png new file mode 100644 index 000000000..119610c99 Binary files /dev/null and b/config/ejoi/2023/media/photos/UKR2.png differ diff --git a/config/ejoi/2023/media/photos/UKR3.png b/config/ejoi/2023/media/photos/UKR3.png new file mode 100644 index 000000000..b7a4a0857 Binary files /dev/null and b/config/ejoi/2023/media/photos/UKR3.png differ diff --git a/config/ejoi/2023/media/photos/UKR4.png b/config/ejoi/2023/media/photos/UKR4.png new file mode 100644 index 000000000..89fc33686 Binary files /dev/null and b/config/ejoi/2023/media/photos/UKR4.png differ diff --git a/config/ejoi/2023/settings.json b/config/ejoi/2023/settings.json new file mode 100644 index 000000000..5fdd1cf3e --- /dev/null +++ b/config/ejoi/2023/settings.json @@ -0,0 +1,6 @@ +{ + "type": "cms", + "activeContest": "day2", + "otherContests": ["day1"], + "url": "http://ranking.ejoi2023.kiu.edu.ge" +} \ No newline at end of file diff --git a/config/icpc-bapc/2022/settings.json b/config/icpc-bapc/2022/settings.json index 1d2821a86..486d05aa0 100644 --- a/config/icpc-bapc/2022/settings.json +++ b/config/icpc-bapc/2022/settings.json @@ -1,6 +1,11 @@ { - "type":"clics", - "login":"$creds.login", - "password":"$creds.password", - "url":"https://cds.gehack.nl/api/contests/bapc2022" + "type": "clics", + "feeds": [ + { + "login": "$creds.login", + "password": "$creds.password", + "url": "https://cds.gehack.nl/api", + "contestId": "bapc2022" + } + ] } diff --git a/config/icpc-nac/2022/practice4/settings.json b/config/icpc-nac/2022/practice4/settings.json index b3a1a59f5..98d922085 100644 --- a/config/icpc-nac/2022/practice4/settings.json +++ b/config/icpc-nac/2022/practice4/settings.json @@ -1,8 +1,13 @@ { - "login":"$creds.kattis_login", - "password":"$creds.kattis_token", - "url":"https://open.kattis.com/clics-api/contests/nac22practice4", - "type":"clics", - "feedVersion":"2020_03", + "type": "clics", + "feeds": [ + { + "url": "https://open.kattis.com/clics-api", + "contestId": "nac22practice4", + "feedVersion": "2020_03", + "login":"$creds.kattis_login", + "password":"$creds.kattis_token" + } + ], "emulation": { "speed": 10, "startTime": "now"} } \ No newline at end of file diff --git a/config/icpc-nac/2023/contest/settings.json b/config/icpc-nac/2023/contest/settings.json index 0a67829d5..51f9dbbb5 100644 --- a/config/icpc-nac/2023/contest/settings.json +++ b/config/icpc-nac/2023/contest/settings.json @@ -1,9 +1,12 @@ { - "type":"clics", - "login":"$creds.cds_login", - "password":"$creds.cds_password", - "url":"https://10.3.3.207/api/contests/NAC-2023-real", - "useTeamNames":false, - - "#media_base_url":"http://10.3.3.207/api" + "type": "clics", + "feeds": [ + { + "login": "$creds.cds_login", + "password": "$creds.cds_password", + "url": "https://10.3.3.207/api", + "contestId": "NAC-2023-real" + } + ], + "useTeamNames": false } \ No newline at end of file diff --git a/config/icpc-nac/2023/dr/settings.json b/config/icpc-nac/2023/dr/settings.json index 8d9c8ca8b..fa9a21b17 100644 --- a/config/icpc-nac/2023/dr/settings.json +++ b/config/icpc-nac/2023/dr/settings.json @@ -1,10 +1,13 @@ { "type":"clics", - "login":"$creds.cds_login", - "password":"$creds.cds_password", - "url":"https://10.3.3.207/api/contests/NAC-2023-dress", - "useTeamNames": false, - - "#media_base_url":"http://10.3.3.207/api" + "feeds": [ + { + "login": "$creds.cds_login", + "password": "$creds.cds_password", + "url": "https://10.3.3.207/api", + "contestId": "NAC-2023-dress" + } + ], + "useTeamNames": false } diff --git a/config/icpc-nac/2023/test1/settings.json b/config/icpc-nac/2023/test1/settings.json index b477b0a1f..3bc594034 100644 --- a/config/icpc-nac/2023/test1/settings.json +++ b/config/icpc-nac/2023/test1/settings.json @@ -1,8 +1,12 @@ { "type": "clics", - "login": "$creds.cds_login", - "password": "$creds.cds_password", - "url": "https://10.3.3.207/api/contests/NAC-2023-test1", - "useTeamNames": false, - "#media_base_url": "http://10.3.3.207/api" + "feeds": [ + { + "login": "$creds.cds_login", + "password": "$creds.cds_password", + "url": "https://10.3.3.207/api", + "contestId": "NAC-2023-test1" + } + ], + "useTeamNames": false } diff --git a/config/icpc-nac/2023/test2/settings.json b/config/icpc-nac/2023/test2/settings.json index 20f8d8d74..ac1efb7ce 100644 --- a/config/icpc-nac/2023/test2/settings.json +++ b/config/icpc-nac/2023/test2/settings.json @@ -1,10 +1,13 @@ { - "type":"clics", - "login":"$creds.cds_login", - "password":"$creds.cds_password", - "url":"https://10.3.3.207/api/contests/NAC-2023-test2", - "useTeamNames":false, - - "#media_base_url":"http://10.3.3.207/api" + "type": "clics", + "feeds": [ + { + "login": "$creds.cds_login", + "password": "$creds.cds_password", + "url": "https://10.3.3.207/api", + "contestId": "NAC-2023-test2" + } + ], + "useTeamNames": false } diff --git a/config/icpc-nwerc/2018/cds/settings.json b/config/icpc-nwerc/2018/cds/settings.json index b5d873c91..673ff896f 100644 --- a/config/icpc-nwerc/2018/cds/settings.json +++ b/config/icpc-nwerc/2018/cds/settings.json @@ -1,6 +1,11 @@ { "type":"clics", - "login":"$creds.nwerc18_login_cds", - "password":"$creds.nwerc18_password_cds", - "url":"https://cds.gehack.nl/api/contests/nwerc18" + "feeds": [ + { + "login": "$creds.nwerc18_login_cds", + "password": "$creds.nwerc18_password_cds", + "url": "https://cds.gehack.nl/api", + "contestId": "nwerc18" + } + ] } diff --git a/config/icpc-nwerc/2018/native/settings.json b/config/icpc-nwerc/2018/native/settings.json index 4f5eb2dbb..8394a0c16 100644 --- a/config/icpc-nwerc/2018/native/settings.json +++ b/config/icpc-nwerc/2018/native/settings.json @@ -1,6 +1,11 @@ { - "type":"clics", - "login":"$creds.nwerc18_login", - "password":"$creds.nwerc18_password", - "url":"https://www.domjudge.org/demoweb/api/contests/nwerc18" + "type": "clics", + "feeds": [ + { + "login": "$creds.nwerc18_login", + "password": "$creds.nwerc18_password", + "url": "https://www.domjudge.org/demoweb/api", + "contestId": "nwerc18" + } + ] } \ No newline at end of file diff --git a/config/icpc-nwerc/2019/settings.json b/config/icpc-nwerc/2019/settings.json index e0628da69..6b667becd 100644 --- a/config/icpc-nwerc/2019/settings.json +++ b/config/icpc-nwerc/2019/settings.json @@ -1,6 +1,11 @@ { "type":"clics", - "login":"live", - "password":"monger-lawless-hymn-inhaler", - "url":"https://judge.gehack.nl:8443/api/contests/3" + "feeds": [ + { + "login": "live", + "password": "monger-lawless-hymn-inhaler", + "url": "https://judge.gehack.nl:8443/api", + "contestId": "3" + } + ] } diff --git a/config/icpc-nwerc/2022/settings.json b/config/icpc-nwerc/2022/settings.json index caff37e1e..313e07974 100644 --- a/config/icpc-nwerc/2022/settings.json +++ b/config/icpc-nwerc/2022/settings.json @@ -1,7 +1,11 @@ { "type":"clics", - "login":"$creds.login", - "password":"$creds.password", - "url":"https://cds.chipcie.ch.tudelft.nl/api/contests/nwerc2022", - "mediaBaseUrl":"https://cds.chipcie.ch.tudelft.nl/api" + "feeds": [ + { + "login": "$creds.login", + "password": "$creds.password", + "url": "https://cds.chipcie.ch.tudelft.nl/api", + "contestId": "nwerc2022" + } + ] } \ No newline at end of file diff --git a/config/icpc-rmc/2021/settings.json b/config/icpc-rmc/2021/settings.json index 973e0e6bf..818669af1 100644 --- a/config/icpc-rmc/2021/settings.json +++ b/config/icpc-rmc/2021/settings.json @@ -1,9 +1,14 @@ { "type":"clics", - "login":"$creds.kattis_login", - "password":"$creds.kattis_token", - "url":"https://open.kattis.com/clics-api/contests/rmc21", - "feedVersion":"2020_03", + "feeds": [ + { + "login": "$creds.kattis_login", + "password": "$creds.kattis_token", + "url": "https://open.kattis.com/clics-api", + "contestId": "rmc21", + "feedVersion": "2020_03" + } + ], "emulation":{"speed":5,"startTime":"2022-05-11 22:40"} } \ No newline at end of file diff --git a/config/icpc-swerc/2021/contest/events.properties b/config/icpc-swerc/2021/contest/events.properties deleted file mode 100644 index 8411eb639..000000000 --- a/config/icpc-swerc/2021/contest/events.properties +++ /dev/null @@ -1,4 +0,0 @@ -login=live -password=secret -url=https://cds.swerc.eu/api/contests/swerc2021 -standings.type=WF \ No newline at end of file diff --git a/config/icpc-swerc/2021/contest/settings.json b/config/icpc-swerc/2021/contest/settings.json new file mode 100644 index 000000000..198b575e1 --- /dev/null +++ b/config/icpc-swerc/2021/contest/settings.json @@ -0,0 +1,11 @@ +{ + "type": "clics", + "feeds": [ + { + "login": "live", + "password": "secret", + "url": "https://cds.swerc.eu/api", + "contestId": "swerc2021" + } + ] +} \ No newline at end of file diff --git a/config/icpc-swerc/2021/dress/settings.json b/config/icpc-swerc/2021/dress/settings.json index ddad5f045..c4e5f6aeb 100644 --- a/config/icpc-swerc/2021/dress/settings.json +++ b/config/icpc-swerc/2021/dress/settings.json @@ -1,7 +1,12 @@ { "type":"clics", - "login":"live", - "password":"SECRET", - "url":"https://cds.swerc.eu/api/contests/demo", - "feedVersion":"2020_03" + "feeds": [ + { + "login": "live", + "password": "SECRET", + "url": "https://cds.swerc.eu/api", + "contestId": "demo", + "feedVersion": "2020_03" + } + ] } \ No newline at end of file diff --git a/config/icpc-swerc/2022/contest/events.properties b/config/icpc-swerc/2022/contest/events.properties deleted file mode 100644 index cc537acc3..000000000 --- a/config/icpc-swerc/2022/contest/events.properties +++ /dev/null @@ -1,4 +0,0 @@ -login=live -password=$creds.swerc22 -url=https://cds.swerc.eu/api/contests/swerc2022 -standings.type=CLICS diff --git a/config/icpc-swerc/2022/contest/settings.json b/config/icpc-swerc/2022/contest/settings.json new file mode 100644 index 000000000..c93b39f89 --- /dev/null +++ b/config/icpc-swerc/2022/contest/settings.json @@ -0,0 +1,11 @@ +{ + "type": "clics", + "feeds": [ + { + "login": "live", + "password": "$creds.swerc22", + "url": "https://cds.swerc.eu/api", + "contestId": "swerc2022" + } + ] +} \ No newline at end of file diff --git a/config/icpc-swerc/2022/mirror/events.properties b/config/icpc-swerc/2022/mirror/events.properties deleted file mode 100644 index 5f8535e4c..000000000 --- a/config/icpc-swerc/2022/mirror/events.properties +++ /dev/null @@ -1,5 +0,0 @@ -cf.api.key=$creds.codeforces_key -cf.api.secret=$creds.codeforces_secret -contest_id=1776 -standings.type=CF - diff --git a/config/icpc-swerc/2022/mirror/settings.json b/config/icpc-swerc/2022/mirror/settings.json new file mode 100644 index 000000000..2a9f00813 --- /dev/null +++ b/config/icpc-swerc/2022/mirror/settings.json @@ -0,0 +1,6 @@ +{ + "type": "cf", + "apiKey": "$creds.codeforces_key", + "apiSecret": "$creds.codeforces_secret", + "contestId": 1776 +} \ No newline at end of file diff --git a/config/icpc-swerc/2022/practice/events.properties b/config/icpc-swerc/2022/practice/events.properties deleted file mode 100644 index 11e49ea50..000000000 --- a/config/icpc-swerc/2022/practice/events.properties +++ /dev/null @@ -1,4 +0,0 @@ -login=live -password=$creds.swerc22 -url=https://cds.swerc.eu/api/contests/practice -standings.type=CLICS diff --git a/config/icpc-swerc/2022/practice/settings.json b/config/icpc-swerc/2022/practice/settings.json new file mode 100644 index 000000000..5f58bbd12 --- /dev/null +++ b/config/icpc-swerc/2022/practice/settings.json @@ -0,0 +1,11 @@ +{ + "type": "clics", + "feeds": [ + { + "login": "live", + "password": "$creds.swerc22", + "url": "https://cds.swerc.eu/api", + "contestId": "practice" + } + ] +} \ No newline at end of file diff --git a/config/icpc-wf/2020/settings.json b/config/icpc-wf/2020/settings.json index b087b5290..d49d0698f 100644 --- a/config/icpc-wf/2020/settings.json +++ b/config/icpc-wf/2020/settings.json @@ -1,8 +1,14 @@ { "type": "clics", - "url": "icpc-wf/2020", + "feeds": [ + { + "url": ".", + "contestId": "", + "eventFeedPath": "", + "feedVersion": "2020_03" + } + ], "useTeamNames": false, - "feedVersion": "2020_03", "emulation": { "speed": 10, "startTime": "now" diff --git a/config/icpc-wf/2021/dress/events.properties b/config/icpc-wf/2021/dress/events.properties deleted file mode 100644 index d6a2038ca..000000000 --- a/config/icpc-wf/2021/dress/events.properties +++ /dev/null @@ -1,8 +0,0 @@ -standings.type=CLICS -url=2021/dress -login=live -password=landed-wafer-state-diner -use_team_names=false -#additional_feed.url=http://172.29.1.215:8099 -#additional_feed.event_feed_name=commentary-messages -#additional_feed.feed_version=2020_03 diff --git a/config/icpc-wf/2021/dress/settings.json b/config/icpc-wf/2021/dress/settings.json new file mode 100644 index 000000000..4d0ee3c49 --- /dev/null +++ b/config/icpc-wf/2021/dress/settings.json @@ -0,0 +1,13 @@ +{ + "type": "clics", + "feeds": [ + { + "url": ".", + "contestId": "", + "eventFeedPath": "", + "login": "live", + "password": "landed-wafer-state-diner" + } + ], + "useTeamNames": false +} \ No newline at end of file diff --git a/config/icpc-wf/2021/finals/events.properties b/config/icpc-wf/2021/finals/events.properties deleted file mode 100644 index 90fa8a484..000000000 --- a/config/icpc-wf/2021/finals/events.properties +++ /dev/null @@ -1,11 +0,0 @@ -standings.type=CLICS -url=icpc-wf/2021/finals -login=live -password=landed-wafer-state-diner -use_team_names=false -additional_feed.url=icpc-wf/2021/finals -additional_feed.event_feed_name=commentary-messages -additional_feed.feed_version=2020_03 - -emulation.startTime=now -emulation.speed=5 diff --git a/config/icpc-wf/2021/finals/settings.json b/config/icpc-wf/2021/finals/settings.json new file mode 100644 index 000000000..1882e2669 --- /dev/null +++ b/config/icpc-wf/2021/finals/settings.json @@ -0,0 +1,20 @@ +{ + "type": "clics", + "feeds": [ + { + "url": ".", + "login": "live", + "contestId": "", + "eventFeedPath": "", + "password": "landed-wafer-state-diner" + }, + { + "url": ".", + "eventFeedName": "commentary-messages", + "contestId": "", + "eventFeedPath": "", + "feedVersion": "2020_03" + } + ], + "useTeamNames": false +} \ No newline at end of file diff --git a/config/icpc-wf/2021/mock/events.properties b/config/icpc-wf/2021/mock/events.properties deleted file mode 100644 index 7b290b927..000000000 --- a/config/icpc-wf/2021/mock/events.properties +++ /dev/null @@ -1,3 +0,0 @@ -standings.type=CLICS -url=https://172.29.1.207:8443/api/contests/mock -use_team_names=false diff --git a/config/icpc-wf/2021/mock/settings.json b/config/icpc-wf/2021/mock/settings.json new file mode 100644 index 000000000..27ddcf24c --- /dev/null +++ b/config/icpc-wf/2021/mock/settings.json @@ -0,0 +1,10 @@ +{ + "type": "clics", + "feeds": [ + { + "url": "https://172.29.1.207:8443/api", + "contestId": "mock" + } + ], + "useTeamNames": false +} diff --git a/config/icpc-wf/2021/pretest/events.properties b/config/icpc-wf/2021/pretest/events.properties deleted file mode 100644 index 4da433f86..000000000 --- a/config/icpc-wf/2021/pretest/events.properties +++ /dev/null @@ -1,4 +0,0 @@ -login=live -password=$creds.pretest_dhaka_pass -url=https://cds.storm.vu/api/contests/pretest_dhaka -standings.type=CLICS diff --git a/config/icpc-wf/2021/pretest/settings.json b/config/icpc-wf/2021/pretest/settings.json new file mode 100644 index 000000000..803954c99 --- /dev/null +++ b/config/icpc-wf/2021/pretest/settings.json @@ -0,0 +1,12 @@ +{ + "type": "clics", + "feeds": [ + { + "url": "https://cds.storm.vu/api", + "password": "$creds.pretest_dhaka_pass", + "login": "live", + "contestId": "pretest_dhaka" + } + ], + "useTeamNames": false +} \ No newline at end of file diff --git a/config/icpc-wf/2021/systest1/events.properties b/config/icpc-wf/2021/systest1/events.properties deleted file mode 100644 index 8f461185d..000000000 --- a/config/icpc-wf/2021/systest1/events.properties +++ /dev/null @@ -1,2 +0,0 @@ -standings.type=CLICS -url=2021/systest1 diff --git a/config/icpc-wf/2021/systest1/settings.json b/config/icpc-wf/2021/systest1/settings.json new file mode 100644 index 000000000..421d14f6f --- /dev/null +++ b/config/icpc-wf/2021/systest1/settings.json @@ -0,0 +1,10 @@ +{ + "type": "clics", + "feeds": [ + { + "url": ".", + "contestId": "", + "eventFeedPath": "" + } + ] +} \ No newline at end of file diff --git a/config/icpc-wf/2021/systest2/events.properties b/config/icpc-wf/2021/systest2/events.properties deleted file mode 100644 index fa0ac1ee9..000000000 --- a/config/icpc-wf/2021/systest2/events.properties +++ /dev/null @@ -1,8 +0,0 @@ -standings.type=CLICS -url=2021/systest2 -login=live -password=landed-wafer-state-diner -use_team_names=false -#additional_feed.url=http://172.29.1.215:8099 -#additional_feed.event_feed_name=commentary-messages -#additional_feed.feed_version=2020_03 diff --git a/config/icpc-wf/2021/systest2/settings.json b/config/icpc-wf/2021/systest2/settings.json new file mode 100644 index 000000000..11828a08f --- /dev/null +++ b/config/icpc-wf/2021/systest2/settings.json @@ -0,0 +1,11 @@ +{ + "type": "clics", + "feeds": [ + { + "url": ".", + "contestId": "", + "eventFeedPath": "" + } + ], + "useTeamNames": false +} \ No newline at end of file diff --git a/config/icpc-wf/2021/test0/events.properties b/config/icpc-wf/2021/test0/events.properties deleted file mode 100644 index 4a30df3a2..000000000 --- a/config/icpc-wf/2021/test0/events.properties +++ /dev/null @@ -1,3 +0,0 @@ -standings.type=CLICS -url=2021/test0 -use_team_names=false diff --git a/config/icpc-wf/2021/test0/settings.json b/config/icpc-wf/2021/test0/settings.json new file mode 100644 index 000000000..11828a08f --- /dev/null +++ b/config/icpc-wf/2021/test0/settings.json @@ -0,0 +1,11 @@ +{ + "type": "clics", + "feeds": [ + { + "url": ".", + "contestId": "", + "eventFeedPath": "" + } + ], + "useTeamNames": false +} \ No newline at end of file diff --git a/config/icpc-wf/2021/test0_1/events.properties b/config/icpc-wf/2021/test0_1/events.properties deleted file mode 100644 index fcc54be56..000000000 --- a/config/icpc-wf/2021/test0_1/events.properties +++ /dev/null @@ -1,3 +0,0 @@ -standings.type=CLICS -url=https://172.29.1.207:8443/api/contests/test0 -use_team_names=false diff --git a/config/icpc-wf/2021/test0_1/settings.json b/config/icpc-wf/2021/test0_1/settings.json new file mode 100644 index 000000000..319674cef --- /dev/null +++ b/config/icpc-wf/2021/test0_1/settings.json @@ -0,0 +1,11 @@ +{ + "type": "clics", + "feeds": [ + { + "url": "https://172.29.1.207:8443/api/contests/test0", + "contestId": "", + "eventFeedPath": "" + } + ], + "useTeamNames": false +} \ No newline at end of file diff --git a/config/icpc-wf/2014/events.xml b/config/icpc-wf/v2-configs/2014/events.xml similarity index 100% rename from config/icpc-wf/2014/events.xml rename to config/icpc-wf/v2-configs/2014/events.xml diff --git a/config/icpc-wf/2014/hashtags.tsv b/config/icpc-wf/v2-configs/2014/hashtags.tsv similarity index 100% rename from config/icpc-wf/2014/hashtags.tsv rename to config/icpc-wf/v2-configs/2014/hashtags.tsv diff --git a/config/icpc-wf/2014/problemset.yaml b/config/icpc-wf/v2-configs/2014/problemset.yaml similarity index 100% rename from config/icpc-wf/2014/problemset.yaml rename to config/icpc-wf/v2-configs/2014/problemset.yaml diff --git a/config/icpc-wf/2014/teams.tsv b/config/icpc-wf/v2-configs/2014/teams.tsv similarity index 100% rename from config/icpc-wf/2014/teams.tsv rename to config/icpc-wf/v2-configs/2014/teams.tsv diff --git a/config/icpc-wf/2015/events-analyst.xml b/config/icpc-wf/v2-configs/2015/events-analyst.xml similarity index 100% rename from config/icpc-wf/2015/events-analyst.xml rename to config/icpc-wf/v2-configs/2015/events-analyst.xml diff --git a/config/icpc-wf/2015/events.xml b/config/icpc-wf/v2-configs/2015/events.xml similarity index 100% rename from config/icpc-wf/2015/events.xml rename to config/icpc-wf/v2-configs/2015/events.xml diff --git a/config/icpc-wf/2015/problemset.yaml b/config/icpc-wf/v2-configs/2015/problemset.yaml similarity index 100% rename from config/icpc-wf/2015/problemset.yaml rename to config/icpc-wf/v2-configs/2015/problemset.yaml diff --git a/config/icpc-wf/2015/teams.tsv b/config/icpc-wf/v2-configs/2015/teams.tsv similarity index 100% rename from config/icpc-wf/2015/teams.tsv rename to config/icpc-wf/v2-configs/2015/teams.tsv diff --git a/config/icpc-wf/2016/events.xml b/config/icpc-wf/v2-configs/2016/events.xml similarity index 100% rename from config/icpc-wf/2016/events.xml rename to config/icpc-wf/v2-configs/2016/events.xml diff --git a/config/icpc-wf/2016/problem.json b/config/icpc-wf/v2-configs/2016/problem.json similarity index 100% rename from config/icpc-wf/2016/problem.json rename to config/icpc-wf/v2-configs/2016/problem.json diff --git a/config/icpc-wf/2016/teams.json b/config/icpc-wf/v2-configs/2016/teams.json similarity index 100% rename from config/icpc-wf/2016/teams.json rename to config/icpc-wf/v2-configs/2016/teams.json diff --git a/config/icpc-wf/2017/contest/events.properties b/config/icpc-wf/v2-configs/2017/contest/events.properties similarity index 100% rename from config/icpc-wf/2017/contest/events.properties rename to config/icpc-wf/v2-configs/2017/contest/events.properties diff --git a/config/icpc-wf/2017/contest/mainscreen.properties b/config/icpc-wf/v2-configs/2017/contest/mainscreen.properties similarity index 100% rename from config/icpc-wf/2017/contest/mainscreen.properties rename to config/icpc-wf/v2-configs/2017/contest/mainscreen.properties diff --git a/config/icpc-wf/2017/contest/teamData/1.json b/config/icpc-wf/v2-configs/2017/contest/teamData/1.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/1.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/1.json diff --git a/config/icpc-wf/2017/contest/teamData/1.png b/config/icpc-wf/v2-configs/2017/contest/teamData/1.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/1.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/1.png diff --git a/config/icpc-wf/2017/contest/teamData/10.json b/config/icpc-wf/v2-configs/2017/contest/teamData/10.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/10.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/10.json diff --git a/config/icpc-wf/2017/contest/teamData/10.png b/config/icpc-wf/v2-configs/2017/contest/teamData/10.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/10.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/10.png diff --git a/config/icpc-wf/2017/contest/teamData/100.json b/config/icpc-wf/v2-configs/2017/contest/teamData/100.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/100.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/100.json diff --git a/config/icpc-wf/2017/contest/teamData/100.png b/config/icpc-wf/v2-configs/2017/contest/teamData/100.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/100.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/100.png diff --git a/config/icpc-wf/2017/contest/teamData/101.json b/config/icpc-wf/v2-configs/2017/contest/teamData/101.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/101.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/101.json diff --git a/config/icpc-wf/2017/contest/teamData/101.png b/config/icpc-wf/v2-configs/2017/contest/teamData/101.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/101.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/101.png diff --git a/config/icpc-wf/2017/contest/teamData/102.json b/config/icpc-wf/v2-configs/2017/contest/teamData/102.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/102.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/102.json diff --git a/config/icpc-wf/2017/contest/teamData/102.png b/config/icpc-wf/v2-configs/2017/contest/teamData/102.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/102.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/102.png diff --git a/config/icpc-wf/2017/contest/teamData/103.json b/config/icpc-wf/v2-configs/2017/contest/teamData/103.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/103.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/103.json diff --git a/config/icpc-wf/2017/contest/teamData/103.png b/config/icpc-wf/v2-configs/2017/contest/teamData/103.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/103.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/103.png diff --git a/config/icpc-wf/2017/contest/teamData/104.json b/config/icpc-wf/v2-configs/2017/contest/teamData/104.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/104.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/104.json diff --git a/config/icpc-wf/2017/contest/teamData/104.png b/config/icpc-wf/v2-configs/2017/contest/teamData/104.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/104.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/104.png diff --git a/config/icpc-wf/2017/contest/teamData/105.json b/config/icpc-wf/v2-configs/2017/contest/teamData/105.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/105.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/105.json diff --git a/config/icpc-wf/2017/contest/teamData/105.png b/config/icpc-wf/v2-configs/2017/contest/teamData/105.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/105.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/105.png diff --git a/config/icpc-wf/2017/contest/teamData/106.json b/config/icpc-wf/v2-configs/2017/contest/teamData/106.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/106.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/106.json diff --git a/config/icpc-wf/2017/contest/teamData/106.png b/config/icpc-wf/v2-configs/2017/contest/teamData/106.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/106.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/106.png diff --git a/config/icpc-wf/2017/contest/teamData/107.json b/config/icpc-wf/v2-configs/2017/contest/teamData/107.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/107.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/107.json diff --git a/config/icpc-wf/2017/contest/teamData/107.png b/config/icpc-wf/v2-configs/2017/contest/teamData/107.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/107.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/107.png diff --git a/config/icpc-wf/2017/contest/teamData/108.json b/config/icpc-wf/v2-configs/2017/contest/teamData/108.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/108.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/108.json diff --git a/config/icpc-wf/2017/contest/teamData/108.png b/config/icpc-wf/v2-configs/2017/contest/teamData/108.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/108.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/108.png diff --git a/config/icpc-wf/2017/contest/teamData/109.json b/config/icpc-wf/v2-configs/2017/contest/teamData/109.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/109.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/109.json diff --git a/config/icpc-wf/2017/contest/teamData/109.png b/config/icpc-wf/v2-configs/2017/contest/teamData/109.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/109.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/109.png diff --git a/config/icpc-wf/2017/contest/teamData/11.json b/config/icpc-wf/v2-configs/2017/contest/teamData/11.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/11.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/11.json diff --git a/config/icpc-wf/2017/contest/teamData/11.png b/config/icpc-wf/v2-configs/2017/contest/teamData/11.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/11.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/11.png diff --git a/config/icpc-wf/2017/contest/teamData/110.json b/config/icpc-wf/v2-configs/2017/contest/teamData/110.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/110.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/110.json diff --git a/config/icpc-wf/2017/contest/teamData/110.png b/config/icpc-wf/v2-configs/2017/contest/teamData/110.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/110.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/110.png diff --git a/config/icpc-wf/2017/contest/teamData/111.json b/config/icpc-wf/v2-configs/2017/contest/teamData/111.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/111.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/111.json diff --git a/config/icpc-wf/2017/contest/teamData/111.png b/config/icpc-wf/v2-configs/2017/contest/teamData/111.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/111.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/111.png diff --git a/config/icpc-wf/2017/contest/teamData/112.json b/config/icpc-wf/v2-configs/2017/contest/teamData/112.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/112.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/112.json diff --git a/config/icpc-wf/2017/contest/teamData/112.png b/config/icpc-wf/v2-configs/2017/contest/teamData/112.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/112.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/112.png diff --git a/config/icpc-wf/2017/contest/teamData/113.json b/config/icpc-wf/v2-configs/2017/contest/teamData/113.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/113.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/113.json diff --git a/config/icpc-wf/2017/contest/teamData/113.png b/config/icpc-wf/v2-configs/2017/contest/teamData/113.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/113.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/113.png diff --git a/config/icpc-wf/2017/contest/teamData/114.json b/config/icpc-wf/v2-configs/2017/contest/teamData/114.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/114.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/114.json diff --git a/config/icpc-wf/2017/contest/teamData/114.png b/config/icpc-wf/v2-configs/2017/contest/teamData/114.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/114.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/114.png diff --git a/config/icpc-wf/2017/contest/teamData/115.json b/config/icpc-wf/v2-configs/2017/contest/teamData/115.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/115.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/115.json diff --git a/config/icpc-wf/2017/contest/teamData/115.png b/config/icpc-wf/v2-configs/2017/contest/teamData/115.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/115.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/115.png diff --git a/config/icpc-wf/2017/contest/teamData/116.json b/config/icpc-wf/v2-configs/2017/contest/teamData/116.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/116.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/116.json diff --git a/config/icpc-wf/2017/contest/teamData/116.png b/config/icpc-wf/v2-configs/2017/contest/teamData/116.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/116.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/116.png diff --git a/config/icpc-wf/2017/contest/teamData/117.json b/config/icpc-wf/v2-configs/2017/contest/teamData/117.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/117.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/117.json diff --git a/config/icpc-wf/2017/contest/teamData/117.png b/config/icpc-wf/v2-configs/2017/contest/teamData/117.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/117.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/117.png diff --git a/config/icpc-wf/2017/contest/teamData/118.json b/config/icpc-wf/v2-configs/2017/contest/teamData/118.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/118.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/118.json diff --git a/config/icpc-wf/2017/contest/teamData/118.png b/config/icpc-wf/v2-configs/2017/contest/teamData/118.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/118.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/118.png diff --git a/config/icpc-wf/2017/contest/teamData/119.json b/config/icpc-wf/v2-configs/2017/contest/teamData/119.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/119.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/119.json diff --git a/config/icpc-wf/2017/contest/teamData/119.png b/config/icpc-wf/v2-configs/2017/contest/teamData/119.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/119.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/119.png diff --git a/config/icpc-wf/2017/contest/teamData/12.json b/config/icpc-wf/v2-configs/2017/contest/teamData/12.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/12.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/12.json diff --git a/config/icpc-wf/2017/contest/teamData/12.png b/config/icpc-wf/v2-configs/2017/contest/teamData/12.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/12.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/12.png diff --git a/config/icpc-wf/2017/contest/teamData/120.json b/config/icpc-wf/v2-configs/2017/contest/teamData/120.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/120.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/120.json diff --git a/config/icpc-wf/2017/contest/teamData/120.png b/config/icpc-wf/v2-configs/2017/contest/teamData/120.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/120.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/120.png diff --git a/config/icpc-wf/2017/contest/teamData/121.json b/config/icpc-wf/v2-configs/2017/contest/teamData/121.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/121.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/121.json diff --git a/config/icpc-wf/2017/contest/teamData/121.png b/config/icpc-wf/v2-configs/2017/contest/teamData/121.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/121.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/121.png diff --git a/config/icpc-wf/2017/contest/teamData/122.json b/config/icpc-wf/v2-configs/2017/contest/teamData/122.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/122.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/122.json diff --git a/config/icpc-wf/2017/contest/teamData/122.png b/config/icpc-wf/v2-configs/2017/contest/teamData/122.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/122.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/122.png diff --git a/config/icpc-wf/2017/contest/teamData/123.json b/config/icpc-wf/v2-configs/2017/contest/teamData/123.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/123.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/123.json diff --git a/config/icpc-wf/2017/contest/teamData/123.png b/config/icpc-wf/v2-configs/2017/contest/teamData/123.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/123.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/123.png diff --git a/config/icpc-wf/2017/contest/teamData/124.json b/config/icpc-wf/v2-configs/2017/contest/teamData/124.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/124.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/124.json diff --git a/config/icpc-wf/2017/contest/teamData/124.png b/config/icpc-wf/v2-configs/2017/contest/teamData/124.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/124.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/124.png diff --git a/config/icpc-wf/2017/contest/teamData/125.json b/config/icpc-wf/v2-configs/2017/contest/teamData/125.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/125.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/125.json diff --git a/config/icpc-wf/2017/contest/teamData/125.png b/config/icpc-wf/v2-configs/2017/contest/teamData/125.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/125.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/125.png diff --git a/config/icpc-wf/2017/contest/teamData/126.json b/config/icpc-wf/v2-configs/2017/contest/teamData/126.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/126.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/126.json diff --git a/config/icpc-wf/2017/contest/teamData/126.png b/config/icpc-wf/v2-configs/2017/contest/teamData/126.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/126.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/126.png diff --git a/config/icpc-wf/2017/contest/teamData/127.json b/config/icpc-wf/v2-configs/2017/contest/teamData/127.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/127.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/127.json diff --git a/config/icpc-wf/2017/contest/teamData/127.png b/config/icpc-wf/v2-configs/2017/contest/teamData/127.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/127.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/127.png diff --git a/config/icpc-wf/2017/contest/teamData/128.json b/config/icpc-wf/v2-configs/2017/contest/teamData/128.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/128.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/128.json diff --git a/config/icpc-wf/2017/contest/teamData/128.png b/config/icpc-wf/v2-configs/2017/contest/teamData/128.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/128.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/128.png diff --git a/config/icpc-wf/2017/contest/teamData/129.json b/config/icpc-wf/v2-configs/2017/contest/teamData/129.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/129.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/129.json diff --git a/config/icpc-wf/2017/contest/teamData/129.png b/config/icpc-wf/v2-configs/2017/contest/teamData/129.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/129.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/129.png diff --git a/config/icpc-wf/2017/contest/teamData/13.json b/config/icpc-wf/v2-configs/2017/contest/teamData/13.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/13.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/13.json diff --git a/config/icpc-wf/2017/contest/teamData/13.png b/config/icpc-wf/v2-configs/2017/contest/teamData/13.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/13.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/13.png diff --git a/config/icpc-wf/2017/contest/teamData/130.json b/config/icpc-wf/v2-configs/2017/contest/teamData/130.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/130.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/130.json diff --git a/config/icpc-wf/2017/contest/teamData/130.png b/config/icpc-wf/v2-configs/2017/contest/teamData/130.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/130.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/130.png diff --git a/config/icpc-wf/2017/contest/teamData/131.json b/config/icpc-wf/v2-configs/2017/contest/teamData/131.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/131.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/131.json diff --git a/config/icpc-wf/2017/contest/teamData/131.png b/config/icpc-wf/v2-configs/2017/contest/teamData/131.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/131.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/131.png diff --git a/config/icpc-wf/2017/contest/teamData/132.json b/config/icpc-wf/v2-configs/2017/contest/teamData/132.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/132.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/132.json diff --git a/config/icpc-wf/2017/contest/teamData/132.png b/config/icpc-wf/v2-configs/2017/contest/teamData/132.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/132.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/132.png diff --git a/config/icpc-wf/2017/contest/teamData/133.json b/config/icpc-wf/v2-configs/2017/contest/teamData/133.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/133.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/133.json diff --git a/config/icpc-wf/2017/contest/teamData/133.png b/config/icpc-wf/v2-configs/2017/contest/teamData/133.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/133.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/133.png diff --git a/config/icpc-wf/2017/contest/teamData/14.json b/config/icpc-wf/v2-configs/2017/contest/teamData/14.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/14.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/14.json diff --git a/config/icpc-wf/2017/contest/teamData/14.png b/config/icpc-wf/v2-configs/2017/contest/teamData/14.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/14.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/14.png diff --git a/config/icpc-wf/2017/contest/teamData/15.json b/config/icpc-wf/v2-configs/2017/contest/teamData/15.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/15.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/15.json diff --git a/config/icpc-wf/2017/contest/teamData/15.png b/config/icpc-wf/v2-configs/2017/contest/teamData/15.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/15.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/15.png diff --git a/config/icpc-wf/2017/contest/teamData/16.json b/config/icpc-wf/v2-configs/2017/contest/teamData/16.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/16.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/16.json diff --git a/config/icpc-wf/2017/contest/teamData/16.png b/config/icpc-wf/v2-configs/2017/contest/teamData/16.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/16.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/16.png diff --git a/config/icpc-wf/2017/contest/teamData/17.json b/config/icpc-wf/v2-configs/2017/contest/teamData/17.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/17.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/17.json diff --git a/config/icpc-wf/2017/contest/teamData/17.png b/config/icpc-wf/v2-configs/2017/contest/teamData/17.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/17.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/17.png diff --git a/config/icpc-wf/2017/contest/teamData/18.json b/config/icpc-wf/v2-configs/2017/contest/teamData/18.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/18.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/18.json diff --git a/config/icpc-wf/2017/contest/teamData/18.png b/config/icpc-wf/v2-configs/2017/contest/teamData/18.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/18.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/18.png diff --git a/config/icpc-wf/2017/contest/teamData/19.json b/config/icpc-wf/v2-configs/2017/contest/teamData/19.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/19.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/19.json diff --git a/config/icpc-wf/2017/contest/teamData/19.png b/config/icpc-wf/v2-configs/2017/contest/teamData/19.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/19.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/19.png diff --git a/config/icpc-wf/2017/contest/teamData/2.json b/config/icpc-wf/v2-configs/2017/contest/teamData/2.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/2.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/2.json diff --git a/config/icpc-wf/2017/contest/teamData/2.png b/config/icpc-wf/v2-configs/2017/contest/teamData/2.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/2.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/2.png diff --git a/config/icpc-wf/2017/contest/teamData/20.json b/config/icpc-wf/v2-configs/2017/contest/teamData/20.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/20.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/20.json diff --git a/config/icpc-wf/2017/contest/teamData/20.png b/config/icpc-wf/v2-configs/2017/contest/teamData/20.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/20.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/20.png diff --git a/config/icpc-wf/2017/contest/teamData/21.json b/config/icpc-wf/v2-configs/2017/contest/teamData/21.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/21.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/21.json diff --git a/config/icpc-wf/2017/contest/teamData/21.png b/config/icpc-wf/v2-configs/2017/contest/teamData/21.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/21.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/21.png diff --git a/config/icpc-wf/2017/contest/teamData/22.json b/config/icpc-wf/v2-configs/2017/contest/teamData/22.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/22.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/22.json diff --git a/config/icpc-wf/2017/contest/teamData/22.png b/config/icpc-wf/v2-configs/2017/contest/teamData/22.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/22.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/22.png diff --git a/config/icpc-wf/2017/contest/teamData/23.json b/config/icpc-wf/v2-configs/2017/contest/teamData/23.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/23.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/23.json diff --git a/config/icpc-wf/2017/contest/teamData/23.png b/config/icpc-wf/v2-configs/2017/contest/teamData/23.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/23.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/23.png diff --git a/config/icpc-wf/2017/contest/teamData/24.json b/config/icpc-wf/v2-configs/2017/contest/teamData/24.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/24.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/24.json diff --git a/config/icpc-wf/2017/contest/teamData/24.png b/config/icpc-wf/v2-configs/2017/contest/teamData/24.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/24.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/24.png diff --git a/config/icpc-wf/2017/contest/teamData/25.json b/config/icpc-wf/v2-configs/2017/contest/teamData/25.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/25.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/25.json diff --git a/config/icpc-wf/2017/contest/teamData/25.png b/config/icpc-wf/v2-configs/2017/contest/teamData/25.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/25.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/25.png diff --git a/config/icpc-wf/2017/contest/teamData/26.json b/config/icpc-wf/v2-configs/2017/contest/teamData/26.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/26.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/26.json diff --git a/config/icpc-wf/2017/contest/teamData/26.png b/config/icpc-wf/v2-configs/2017/contest/teamData/26.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/26.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/26.png diff --git a/config/icpc-wf/2017/contest/teamData/27.json b/config/icpc-wf/v2-configs/2017/contest/teamData/27.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/27.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/27.json diff --git a/config/icpc-wf/2017/contest/teamData/27.png b/config/icpc-wf/v2-configs/2017/contest/teamData/27.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/27.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/27.png diff --git a/config/icpc-wf/2017/contest/teamData/28.json b/config/icpc-wf/v2-configs/2017/contest/teamData/28.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/28.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/28.json diff --git a/config/icpc-wf/2017/contest/teamData/28.png b/config/icpc-wf/v2-configs/2017/contest/teamData/28.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/28.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/28.png diff --git a/config/icpc-wf/2017/contest/teamData/29.json b/config/icpc-wf/v2-configs/2017/contest/teamData/29.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/29.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/29.json diff --git a/config/icpc-wf/2017/contest/teamData/29.png b/config/icpc-wf/v2-configs/2017/contest/teamData/29.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/29.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/29.png diff --git a/config/icpc-wf/2017/contest/teamData/3.json b/config/icpc-wf/v2-configs/2017/contest/teamData/3.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/3.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/3.json diff --git a/config/icpc-wf/2017/contest/teamData/3.png b/config/icpc-wf/v2-configs/2017/contest/teamData/3.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/3.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/3.png diff --git a/config/icpc-wf/2017/contest/teamData/30.json b/config/icpc-wf/v2-configs/2017/contest/teamData/30.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/30.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/30.json diff --git a/config/icpc-wf/2017/contest/teamData/30.png b/config/icpc-wf/v2-configs/2017/contest/teamData/30.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/30.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/30.png diff --git a/config/icpc-wf/2017/contest/teamData/31.json b/config/icpc-wf/v2-configs/2017/contest/teamData/31.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/31.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/31.json diff --git a/config/icpc-wf/2017/contest/teamData/31.png b/config/icpc-wf/v2-configs/2017/contest/teamData/31.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/31.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/31.png diff --git a/config/icpc-wf/2017/contest/teamData/32.json b/config/icpc-wf/v2-configs/2017/contest/teamData/32.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/32.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/32.json diff --git a/config/icpc-wf/2017/contest/teamData/32.png b/config/icpc-wf/v2-configs/2017/contest/teamData/32.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/32.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/32.png diff --git a/config/icpc-wf/2017/contest/teamData/33.json b/config/icpc-wf/v2-configs/2017/contest/teamData/33.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/33.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/33.json diff --git a/config/icpc-wf/2017/contest/teamData/33.png b/config/icpc-wf/v2-configs/2017/contest/teamData/33.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/33.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/33.png diff --git a/config/icpc-wf/2017/contest/teamData/34.json b/config/icpc-wf/v2-configs/2017/contest/teamData/34.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/34.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/34.json diff --git a/config/icpc-wf/2017/contest/teamData/34.png b/config/icpc-wf/v2-configs/2017/contest/teamData/34.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/34.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/34.png diff --git a/config/icpc-wf/2017/contest/teamData/35.json b/config/icpc-wf/v2-configs/2017/contest/teamData/35.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/35.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/35.json diff --git a/config/icpc-wf/2017/contest/teamData/35.png b/config/icpc-wf/v2-configs/2017/contest/teamData/35.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/35.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/35.png diff --git a/config/icpc-wf/2017/contest/teamData/36.json b/config/icpc-wf/v2-configs/2017/contest/teamData/36.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/36.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/36.json diff --git a/config/icpc-wf/2017/contest/teamData/36.png b/config/icpc-wf/v2-configs/2017/contest/teamData/36.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/36.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/36.png diff --git a/config/icpc-wf/2017/contest/teamData/37.json b/config/icpc-wf/v2-configs/2017/contest/teamData/37.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/37.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/37.json diff --git a/config/icpc-wf/2017/contest/teamData/37.png b/config/icpc-wf/v2-configs/2017/contest/teamData/37.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/37.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/37.png diff --git a/config/icpc-wf/2017/contest/teamData/38.json b/config/icpc-wf/v2-configs/2017/contest/teamData/38.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/38.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/38.json diff --git a/config/icpc-wf/2017/contest/teamData/38.png b/config/icpc-wf/v2-configs/2017/contest/teamData/38.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/38.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/38.png diff --git a/config/icpc-wf/2017/contest/teamData/39.json b/config/icpc-wf/v2-configs/2017/contest/teamData/39.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/39.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/39.json diff --git a/config/icpc-wf/2017/contest/teamData/39.png b/config/icpc-wf/v2-configs/2017/contest/teamData/39.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/39.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/39.png diff --git a/config/icpc-wf/2017/contest/teamData/4.json b/config/icpc-wf/v2-configs/2017/contest/teamData/4.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/4.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/4.json diff --git a/config/icpc-wf/2017/contest/teamData/4.png b/config/icpc-wf/v2-configs/2017/contest/teamData/4.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/4.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/4.png diff --git a/config/icpc-wf/2017/contest/teamData/40.json b/config/icpc-wf/v2-configs/2017/contest/teamData/40.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/40.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/40.json diff --git a/config/icpc-wf/2017/contest/teamData/40.png b/config/icpc-wf/v2-configs/2017/contest/teamData/40.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/40.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/40.png diff --git a/config/icpc-wf/2017/contest/teamData/41.json b/config/icpc-wf/v2-configs/2017/contest/teamData/41.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/41.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/41.json diff --git a/config/icpc-wf/2017/contest/teamData/41.png b/config/icpc-wf/v2-configs/2017/contest/teamData/41.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/41.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/41.png diff --git a/config/icpc-wf/2017/contest/teamData/42.json b/config/icpc-wf/v2-configs/2017/contest/teamData/42.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/42.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/42.json diff --git a/config/icpc-wf/2017/contest/teamData/42.png b/config/icpc-wf/v2-configs/2017/contest/teamData/42.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/42.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/42.png diff --git a/config/icpc-wf/2017/contest/teamData/43.json b/config/icpc-wf/v2-configs/2017/contest/teamData/43.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/43.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/43.json diff --git a/config/icpc-wf/2017/contest/teamData/43.png b/config/icpc-wf/v2-configs/2017/contest/teamData/43.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/43.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/43.png diff --git a/config/icpc-wf/2017/contest/teamData/44.json b/config/icpc-wf/v2-configs/2017/contest/teamData/44.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/44.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/44.json diff --git a/config/icpc-wf/2017/contest/teamData/44.png b/config/icpc-wf/v2-configs/2017/contest/teamData/44.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/44.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/44.png diff --git a/config/icpc-wf/2017/contest/teamData/45.json b/config/icpc-wf/v2-configs/2017/contest/teamData/45.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/45.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/45.json diff --git a/config/icpc-wf/2017/contest/teamData/45.png b/config/icpc-wf/v2-configs/2017/contest/teamData/45.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/45.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/45.png diff --git a/config/icpc-wf/2017/contest/teamData/46.json b/config/icpc-wf/v2-configs/2017/contest/teamData/46.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/46.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/46.json diff --git a/config/icpc-wf/2017/contest/teamData/46.png b/config/icpc-wf/v2-configs/2017/contest/teamData/46.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/46.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/46.png diff --git a/config/icpc-wf/2017/contest/teamData/47.json b/config/icpc-wf/v2-configs/2017/contest/teamData/47.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/47.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/47.json diff --git a/config/icpc-wf/2017/contest/teamData/47.png b/config/icpc-wf/v2-configs/2017/contest/teamData/47.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/47.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/47.png diff --git a/config/icpc-wf/2017/contest/teamData/48.json b/config/icpc-wf/v2-configs/2017/contest/teamData/48.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/48.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/48.json diff --git a/config/icpc-wf/2017/contest/teamData/48.png b/config/icpc-wf/v2-configs/2017/contest/teamData/48.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/48.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/48.png diff --git a/config/icpc-wf/2017/contest/teamData/49.json b/config/icpc-wf/v2-configs/2017/contest/teamData/49.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/49.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/49.json diff --git a/config/icpc-wf/2017/contest/teamData/49.png b/config/icpc-wf/v2-configs/2017/contest/teamData/49.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/49.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/49.png diff --git a/config/icpc-wf/2017/contest/teamData/5.json b/config/icpc-wf/v2-configs/2017/contest/teamData/5.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/5.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/5.json diff --git a/config/icpc-wf/2017/contest/teamData/5.png b/config/icpc-wf/v2-configs/2017/contest/teamData/5.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/5.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/5.png diff --git a/config/icpc-wf/2017/contest/teamData/50.json b/config/icpc-wf/v2-configs/2017/contest/teamData/50.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/50.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/50.json diff --git a/config/icpc-wf/2017/contest/teamData/50.png b/config/icpc-wf/v2-configs/2017/contest/teamData/50.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/50.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/50.png diff --git a/config/icpc-wf/2017/contest/teamData/51.json b/config/icpc-wf/v2-configs/2017/contest/teamData/51.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/51.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/51.json diff --git a/config/icpc-wf/2017/contest/teamData/51.png b/config/icpc-wf/v2-configs/2017/contest/teamData/51.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/51.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/51.png diff --git a/config/icpc-wf/2017/contest/teamData/52.json b/config/icpc-wf/v2-configs/2017/contest/teamData/52.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/52.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/52.json diff --git a/config/icpc-wf/2017/contest/teamData/52.png b/config/icpc-wf/v2-configs/2017/contest/teamData/52.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/52.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/52.png diff --git a/config/icpc-wf/2017/contest/teamData/53.json b/config/icpc-wf/v2-configs/2017/contest/teamData/53.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/53.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/53.json diff --git a/config/icpc-wf/2017/contest/teamData/53.png b/config/icpc-wf/v2-configs/2017/contest/teamData/53.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/53.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/53.png diff --git a/config/icpc-wf/2017/contest/teamData/54.json b/config/icpc-wf/v2-configs/2017/contest/teamData/54.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/54.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/54.json diff --git a/config/icpc-wf/2017/contest/teamData/54.png b/config/icpc-wf/v2-configs/2017/contest/teamData/54.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/54.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/54.png diff --git a/config/icpc-wf/2017/contest/teamData/55.json b/config/icpc-wf/v2-configs/2017/contest/teamData/55.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/55.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/55.json diff --git a/config/icpc-wf/2017/contest/teamData/55.png b/config/icpc-wf/v2-configs/2017/contest/teamData/55.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/55.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/55.png diff --git a/config/icpc-wf/2017/contest/teamData/56.json b/config/icpc-wf/v2-configs/2017/contest/teamData/56.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/56.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/56.json diff --git a/config/icpc-wf/2017/contest/teamData/56.png b/config/icpc-wf/v2-configs/2017/contest/teamData/56.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/56.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/56.png diff --git a/config/icpc-wf/2017/contest/teamData/57.json b/config/icpc-wf/v2-configs/2017/contest/teamData/57.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/57.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/57.json diff --git a/config/icpc-wf/2017/contest/teamData/57.png b/config/icpc-wf/v2-configs/2017/contest/teamData/57.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/57.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/57.png diff --git a/config/icpc-wf/2017/contest/teamData/58.json b/config/icpc-wf/v2-configs/2017/contest/teamData/58.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/58.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/58.json diff --git a/config/icpc-wf/2017/contest/teamData/58.png b/config/icpc-wf/v2-configs/2017/contest/teamData/58.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/58.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/58.png diff --git a/config/icpc-wf/2017/contest/teamData/59.json b/config/icpc-wf/v2-configs/2017/contest/teamData/59.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/59.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/59.json diff --git a/config/icpc-wf/2017/contest/teamData/59.png b/config/icpc-wf/v2-configs/2017/contest/teamData/59.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/59.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/59.png diff --git a/config/icpc-wf/2017/contest/teamData/6.json b/config/icpc-wf/v2-configs/2017/contest/teamData/6.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/6.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/6.json diff --git a/config/icpc-wf/2017/contest/teamData/6.png b/config/icpc-wf/v2-configs/2017/contest/teamData/6.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/6.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/6.png diff --git a/config/icpc-wf/2017/contest/teamData/60.json b/config/icpc-wf/v2-configs/2017/contest/teamData/60.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/60.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/60.json diff --git a/config/icpc-wf/2017/contest/teamData/60.png b/config/icpc-wf/v2-configs/2017/contest/teamData/60.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/60.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/60.png diff --git a/config/icpc-wf/2017/contest/teamData/61.json b/config/icpc-wf/v2-configs/2017/contest/teamData/61.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/61.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/61.json diff --git a/config/icpc-wf/2017/contest/teamData/61.png b/config/icpc-wf/v2-configs/2017/contest/teamData/61.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/61.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/61.png diff --git a/config/icpc-wf/2017/contest/teamData/62.json b/config/icpc-wf/v2-configs/2017/contest/teamData/62.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/62.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/62.json diff --git a/config/icpc-wf/2017/contest/teamData/62.png b/config/icpc-wf/v2-configs/2017/contest/teamData/62.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/62.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/62.png diff --git a/config/icpc-wf/2017/contest/teamData/63.json b/config/icpc-wf/v2-configs/2017/contest/teamData/63.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/63.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/63.json diff --git a/config/icpc-wf/2017/contest/teamData/63.png b/config/icpc-wf/v2-configs/2017/contest/teamData/63.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/63.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/63.png diff --git a/config/icpc-wf/2017/contest/teamData/64.json b/config/icpc-wf/v2-configs/2017/contest/teamData/64.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/64.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/64.json diff --git a/config/icpc-wf/2017/contest/teamData/64.png b/config/icpc-wf/v2-configs/2017/contest/teamData/64.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/64.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/64.png diff --git a/config/icpc-wf/2017/contest/teamData/65.json b/config/icpc-wf/v2-configs/2017/contest/teamData/65.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/65.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/65.json diff --git a/config/icpc-wf/2017/contest/teamData/65.png b/config/icpc-wf/v2-configs/2017/contest/teamData/65.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/65.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/65.png diff --git a/config/icpc-wf/2017/contest/teamData/66.json b/config/icpc-wf/v2-configs/2017/contest/teamData/66.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/66.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/66.json diff --git a/config/icpc-wf/2017/contest/teamData/66.png b/config/icpc-wf/v2-configs/2017/contest/teamData/66.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/66.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/66.png diff --git a/config/icpc-wf/2017/contest/teamData/67.json b/config/icpc-wf/v2-configs/2017/contest/teamData/67.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/67.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/67.json diff --git a/config/icpc-wf/2017/contest/teamData/67.png b/config/icpc-wf/v2-configs/2017/contest/teamData/67.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/67.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/67.png diff --git a/config/icpc-wf/2017/contest/teamData/68.json b/config/icpc-wf/v2-configs/2017/contest/teamData/68.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/68.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/68.json diff --git a/config/icpc-wf/2017/contest/teamData/68.png b/config/icpc-wf/v2-configs/2017/contest/teamData/68.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/68.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/68.png diff --git a/config/icpc-wf/2017/contest/teamData/69.json b/config/icpc-wf/v2-configs/2017/contest/teamData/69.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/69.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/69.json diff --git a/config/icpc-wf/2017/contest/teamData/69.png b/config/icpc-wf/v2-configs/2017/contest/teamData/69.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/69.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/69.png diff --git a/config/icpc-wf/2017/contest/teamData/7.json b/config/icpc-wf/v2-configs/2017/contest/teamData/7.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/7.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/7.json diff --git a/config/icpc-wf/2017/contest/teamData/7.png b/config/icpc-wf/v2-configs/2017/contest/teamData/7.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/7.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/7.png diff --git a/config/icpc-wf/2017/contest/teamData/70.json b/config/icpc-wf/v2-configs/2017/contest/teamData/70.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/70.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/70.json diff --git a/config/icpc-wf/2017/contest/teamData/70.png b/config/icpc-wf/v2-configs/2017/contest/teamData/70.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/70.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/70.png diff --git a/config/icpc-wf/2017/contest/teamData/71.json b/config/icpc-wf/v2-configs/2017/contest/teamData/71.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/71.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/71.json diff --git a/config/icpc-wf/2017/contest/teamData/71.png b/config/icpc-wf/v2-configs/2017/contest/teamData/71.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/71.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/71.png diff --git a/config/icpc-wf/2017/contest/teamData/72.json b/config/icpc-wf/v2-configs/2017/contest/teamData/72.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/72.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/72.json diff --git a/config/icpc-wf/2017/contest/teamData/72.png b/config/icpc-wf/v2-configs/2017/contest/teamData/72.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/72.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/72.png diff --git a/config/icpc-wf/2017/contest/teamData/73.json b/config/icpc-wf/v2-configs/2017/contest/teamData/73.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/73.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/73.json diff --git a/config/icpc-wf/2017/contest/teamData/73.png b/config/icpc-wf/v2-configs/2017/contest/teamData/73.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/73.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/73.png diff --git a/config/icpc-wf/2017/contest/teamData/74.json b/config/icpc-wf/v2-configs/2017/contest/teamData/74.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/74.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/74.json diff --git a/config/icpc-wf/2017/contest/teamData/74.png b/config/icpc-wf/v2-configs/2017/contest/teamData/74.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/74.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/74.png diff --git a/config/icpc-wf/2017/contest/teamData/75.json b/config/icpc-wf/v2-configs/2017/contest/teamData/75.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/75.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/75.json diff --git a/config/icpc-wf/2017/contest/teamData/75.png b/config/icpc-wf/v2-configs/2017/contest/teamData/75.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/75.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/75.png diff --git a/config/icpc-wf/2017/contest/teamData/76.json b/config/icpc-wf/v2-configs/2017/contest/teamData/76.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/76.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/76.json diff --git a/config/icpc-wf/2017/contest/teamData/76.png b/config/icpc-wf/v2-configs/2017/contest/teamData/76.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/76.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/76.png diff --git a/config/icpc-wf/2017/contest/teamData/77.json b/config/icpc-wf/v2-configs/2017/contest/teamData/77.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/77.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/77.json diff --git a/config/icpc-wf/2017/contest/teamData/77.png b/config/icpc-wf/v2-configs/2017/contest/teamData/77.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/77.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/77.png diff --git a/config/icpc-wf/2017/contest/teamData/78.json b/config/icpc-wf/v2-configs/2017/contest/teamData/78.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/78.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/78.json diff --git a/config/icpc-wf/2017/contest/teamData/78.png b/config/icpc-wf/v2-configs/2017/contest/teamData/78.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/78.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/78.png diff --git a/config/icpc-wf/2017/contest/teamData/79.json b/config/icpc-wf/v2-configs/2017/contest/teamData/79.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/79.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/79.json diff --git a/config/icpc-wf/2017/contest/teamData/79.png b/config/icpc-wf/v2-configs/2017/contest/teamData/79.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/79.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/79.png diff --git a/config/icpc-wf/2017/contest/teamData/8.json b/config/icpc-wf/v2-configs/2017/contest/teamData/8.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/8.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/8.json diff --git a/config/icpc-wf/2017/contest/teamData/8.png b/config/icpc-wf/v2-configs/2017/contest/teamData/8.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/8.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/8.png diff --git a/config/icpc-wf/2017/contest/teamData/80.json b/config/icpc-wf/v2-configs/2017/contest/teamData/80.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/80.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/80.json diff --git a/config/icpc-wf/2017/contest/teamData/80.png b/config/icpc-wf/v2-configs/2017/contest/teamData/80.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/80.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/80.png diff --git a/config/icpc-wf/2017/contest/teamData/81.json b/config/icpc-wf/v2-configs/2017/contest/teamData/81.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/81.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/81.json diff --git a/config/icpc-wf/2017/contest/teamData/81.png b/config/icpc-wf/v2-configs/2017/contest/teamData/81.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/81.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/81.png diff --git a/config/icpc-wf/2017/contest/teamData/82.json b/config/icpc-wf/v2-configs/2017/contest/teamData/82.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/82.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/82.json diff --git a/config/icpc-wf/2017/contest/teamData/82.png b/config/icpc-wf/v2-configs/2017/contest/teamData/82.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/82.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/82.png diff --git a/config/icpc-wf/2017/contest/teamData/83.json b/config/icpc-wf/v2-configs/2017/contest/teamData/83.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/83.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/83.json diff --git a/config/icpc-wf/2017/contest/teamData/83.png b/config/icpc-wf/v2-configs/2017/contest/teamData/83.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/83.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/83.png diff --git a/config/icpc-wf/2017/contest/teamData/84.json b/config/icpc-wf/v2-configs/2017/contest/teamData/84.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/84.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/84.json diff --git a/config/icpc-wf/2017/contest/teamData/84.png b/config/icpc-wf/v2-configs/2017/contest/teamData/84.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/84.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/84.png diff --git a/config/icpc-wf/2017/contest/teamData/85.json b/config/icpc-wf/v2-configs/2017/contest/teamData/85.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/85.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/85.json diff --git a/config/icpc-wf/2017/contest/teamData/85.png b/config/icpc-wf/v2-configs/2017/contest/teamData/85.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/85.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/85.png diff --git a/config/icpc-wf/2017/contest/teamData/86.json b/config/icpc-wf/v2-configs/2017/contest/teamData/86.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/86.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/86.json diff --git a/config/icpc-wf/2017/contest/teamData/86.png b/config/icpc-wf/v2-configs/2017/contest/teamData/86.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/86.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/86.png diff --git a/config/icpc-wf/2017/contest/teamData/87.json b/config/icpc-wf/v2-configs/2017/contest/teamData/87.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/87.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/87.json diff --git a/config/icpc-wf/2017/contest/teamData/87.png b/config/icpc-wf/v2-configs/2017/contest/teamData/87.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/87.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/87.png diff --git a/config/icpc-wf/2017/contest/teamData/88.json b/config/icpc-wf/v2-configs/2017/contest/teamData/88.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/88.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/88.json diff --git a/config/icpc-wf/2017/contest/teamData/88.png b/config/icpc-wf/v2-configs/2017/contest/teamData/88.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/88.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/88.png diff --git a/config/icpc-wf/2017/contest/teamData/89.json b/config/icpc-wf/v2-configs/2017/contest/teamData/89.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/89.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/89.json diff --git a/config/icpc-wf/2017/contest/teamData/89.png b/config/icpc-wf/v2-configs/2017/contest/teamData/89.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/89.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/89.png diff --git a/config/icpc-wf/2017/contest/teamData/9.json b/config/icpc-wf/v2-configs/2017/contest/teamData/9.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/9.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/9.json diff --git a/config/icpc-wf/2017/contest/teamData/9.png b/config/icpc-wf/v2-configs/2017/contest/teamData/9.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/9.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/9.png diff --git a/config/icpc-wf/2017/contest/teamData/90.json b/config/icpc-wf/v2-configs/2017/contest/teamData/90.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/90.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/90.json diff --git a/config/icpc-wf/2017/contest/teamData/90.png b/config/icpc-wf/v2-configs/2017/contest/teamData/90.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/90.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/90.png diff --git a/config/icpc-wf/2017/contest/teamData/91.json b/config/icpc-wf/v2-configs/2017/contest/teamData/91.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/91.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/91.json diff --git a/config/icpc-wf/2017/contest/teamData/91.png b/config/icpc-wf/v2-configs/2017/contest/teamData/91.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/91.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/91.png diff --git a/config/icpc-wf/2017/contest/teamData/92.json b/config/icpc-wf/v2-configs/2017/contest/teamData/92.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/92.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/92.json diff --git a/config/icpc-wf/2017/contest/teamData/92.png b/config/icpc-wf/v2-configs/2017/contest/teamData/92.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/92.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/92.png diff --git a/config/icpc-wf/2017/contest/teamData/93.json b/config/icpc-wf/v2-configs/2017/contest/teamData/93.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/93.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/93.json diff --git a/config/icpc-wf/2017/contest/teamData/93.png b/config/icpc-wf/v2-configs/2017/contest/teamData/93.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/93.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/93.png diff --git a/config/icpc-wf/2017/contest/teamData/94.json b/config/icpc-wf/v2-configs/2017/contest/teamData/94.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/94.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/94.json diff --git a/config/icpc-wf/2017/contest/teamData/94.png b/config/icpc-wf/v2-configs/2017/contest/teamData/94.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/94.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/94.png diff --git a/config/icpc-wf/2017/contest/teamData/95.json b/config/icpc-wf/v2-configs/2017/contest/teamData/95.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/95.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/95.json diff --git a/config/icpc-wf/2017/contest/teamData/95.png b/config/icpc-wf/v2-configs/2017/contest/teamData/95.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/95.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/95.png diff --git a/config/icpc-wf/2017/contest/teamData/96.json b/config/icpc-wf/v2-configs/2017/contest/teamData/96.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/96.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/96.json diff --git a/config/icpc-wf/2017/contest/teamData/96.png b/config/icpc-wf/v2-configs/2017/contest/teamData/96.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/96.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/96.png diff --git a/config/icpc-wf/2017/contest/teamData/97.json b/config/icpc-wf/v2-configs/2017/contest/teamData/97.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/97.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/97.json diff --git a/config/icpc-wf/2017/contest/teamData/97.png b/config/icpc-wf/v2-configs/2017/contest/teamData/97.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/97.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/97.png diff --git a/config/icpc-wf/2017/contest/teamData/98.json b/config/icpc-wf/v2-configs/2017/contest/teamData/98.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/98.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/98.json diff --git a/config/icpc-wf/2017/contest/teamData/98.png b/config/icpc-wf/v2-configs/2017/contest/teamData/98.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/98.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/98.png diff --git a/config/icpc-wf/2017/contest/teamData/99.json b/config/icpc-wf/v2-configs/2017/contest/teamData/99.json similarity index 100% rename from config/icpc-wf/2017/contest/teamData/99.json rename to config/icpc-wf/v2-configs/2017/contest/teamData/99.json diff --git a/config/icpc-wf/2017/contest/teamData/99.png b/config/icpc-wf/v2-configs/2017/contest/teamData/99.png similarity index 100% rename from config/icpc-wf/2017/contest/teamData/99.png rename to config/icpc-wf/v2-configs/2017/contest/teamData/99.png diff --git a/config/icpc-wf/2017/test-1/cds.events.properties b/config/icpc-wf/v2-configs/2017/test-1/cds.events.properties similarity index 100% rename from config/icpc-wf/2017/test-1/cds.events.properties rename to config/icpc-wf/v2-configs/2017/test-1/cds.events.properties diff --git a/config/icpc-wf/2017/test-1/cds.mainscreen.properties b/config/icpc-wf/v2-configs/2017/test-1/cds.mainscreen.properties similarity index 100% rename from config/icpc-wf/2017/test-1/cds.mainscreen.properties rename to config/icpc-wf/v2-configs/2017/test-1/cds.mainscreen.properties diff --git a/config/icpc-wf/2017/test-1/contest.json b/config/icpc-wf/v2-configs/2017/test-1/contest.json similarity index 100% rename from config/icpc-wf/2017/test-1/contest.json rename to config/icpc-wf/v2-configs/2017/test-1/contest.json diff --git a/config/icpc-wf/2018/contest/event-feed b/config/icpc-wf/v2-configs/2018/contest/event-feed similarity index 100% rename from config/icpc-wf/2018/contest/event-feed rename to config/icpc-wf/v2-configs/2018/contest/event-feed diff --git a/config/icpc-wf/2018/contest/events.properties b/config/icpc-wf/v2-configs/2018/contest/events.properties similarity index 100% rename from config/icpc-wf/2018/contest/events.properties rename to config/icpc-wf/v2-configs/2018/contest/events.properties diff --git a/config/icpc-wf/2018/contest/groups b/config/icpc-wf/v2-configs/2018/contest/groups similarity index 100% rename from config/icpc-wf/2018/contest/groups rename to config/icpc-wf/v2-configs/2018/contest/groups diff --git a/config/icpc-wf/2018/contest/languages b/config/icpc-wf/v2-configs/2018/contest/languages similarity index 100% rename from config/icpc-wf/2018/contest/languages rename to config/icpc-wf/v2-configs/2018/contest/languages diff --git a/config/icpc-wf/2018/contest/mainscreen.properties b/config/icpc-wf/v2-configs/2018/contest/mainscreen.properties similarity index 100% rename from config/icpc-wf/2018/contest/mainscreen.properties rename to config/icpc-wf/v2-configs/2018/contest/mainscreen.properties diff --git a/config/icpc-wf/2018/contest/organizations b/config/icpc-wf/v2-configs/2018/contest/organizations similarity index 100% rename from config/icpc-wf/2018/contest/organizations rename to config/icpc-wf/v2-configs/2018/contest/organizations diff --git a/config/icpc-wf/2018/contest/problems b/config/icpc-wf/v2-configs/2018/contest/problems similarity index 100% rename from config/icpc-wf/2018/contest/problems rename to config/icpc-wf/v2-configs/2018/contest/problems diff --git a/config/icpc-wf/2018/contest/scoreboard b/config/icpc-wf/v2-configs/2018/contest/scoreboard similarity index 100% rename from config/icpc-wf/2018/contest/scoreboard rename to config/icpc-wf/v2-configs/2018/contest/scoreboard diff --git a/config/icpc-wf/2018/contest/teams b/config/icpc-wf/v2-configs/2018/contest/teams similarity index 100% rename from config/icpc-wf/2018/contest/teams rename to config/icpc-wf/v2-configs/2018/contest/teams diff --git a/config/icpc-wf/2018/test/event-feed b/config/icpc-wf/v2-configs/2018/test/event-feed similarity index 100% rename from config/icpc-wf/2018/test/event-feed rename to config/icpc-wf/v2-configs/2018/test/event-feed diff --git a/config/icpc-wf/2018/test/groups b/config/icpc-wf/v2-configs/2018/test/groups similarity index 100% rename from config/icpc-wf/2018/test/groups rename to config/icpc-wf/v2-configs/2018/test/groups diff --git a/config/icpc-wf/2018/test/languages b/config/icpc-wf/v2-configs/2018/test/languages similarity index 100% rename from config/icpc-wf/2018/test/languages rename to config/icpc-wf/v2-configs/2018/test/languages diff --git a/config/icpc-wf/2018/test/organizations b/config/icpc-wf/v2-configs/2018/test/organizations similarity index 100% rename from config/icpc-wf/2018/test/organizations rename to config/icpc-wf/v2-configs/2018/test/organizations diff --git a/config/icpc-wf/2018/test/problems b/config/icpc-wf/v2-configs/2018/test/problems similarity index 100% rename from config/icpc-wf/2018/test/problems rename to config/icpc-wf/v2-configs/2018/test/problems diff --git a/config/icpc-wf/2018/test/teams b/config/icpc-wf/v2-configs/2018/test/teams similarity index 100% rename from config/icpc-wf/2018/test/teams rename to config/icpc-wf/v2-configs/2018/test/teams diff --git a/config/icpc-wf/2019/contest/admin-event-feed b/config/icpc-wf/v2-configs/2019/contest/admin-event-feed similarity index 100% rename from config/icpc-wf/2019/contest/admin-event-feed rename to config/icpc-wf/v2-configs/2019/contest/admin-event-feed diff --git a/config/icpc-wf/2019/contest/event-feed b/config/icpc-wf/v2-configs/2019/contest/event-feed similarity index 100% rename from config/icpc-wf/2019/contest/event-feed rename to config/icpc-wf/v2-configs/2019/contest/event-feed diff --git a/config/icpc-wf/2019/contest/events.properties b/config/icpc-wf/v2-configs/2019/contest/events.properties similarity index 100% rename from config/icpc-wf/2019/contest/events.properties rename to config/icpc-wf/v2-configs/2019/contest/events.properties diff --git a/config/icpc-wf/2019/contest/mainscreen.properties b/config/icpc-wf/v2-configs/2019/contest/mainscreen.properties similarity index 100% rename from config/icpc-wf/2019/contest/mainscreen.properties rename to config/icpc-wf/v2-configs/2019/contest/mainscreen.properties diff --git a/config/icpc-wf/2019/test/events.properties b/config/icpc-wf/v2-configs/2019/test/events.properties similarity index 100% rename from config/icpc-wf/2019/test/events.properties rename to config/icpc-wf/v2-configs/2019/test/events.properties diff --git a/config/icpc-wf/2019/test/mainscreen.properties b/config/icpc-wf/v2-configs/2019/test/mainscreen.properties similarity index 100% rename from config/icpc-wf/2019/test/mainscreen.properties rename to config/icpc-wf/v2-configs/2019/test/mainscreen.properties diff --git a/config/icpc-wf/2019/test/splitscreen.properties b/config/icpc-wf/v2-configs/2019/test/splitscreen.properties similarity index 100% rename from config/icpc-wf/2019/test/splitscreen.properties rename to config/icpc-wf/v2-configs/2019/test/splitscreen.properties diff --git a/config/regionalroi/spb-2022-d1/settings.json b/config/regionalroi/spb-2022-d1/settings.json index ab1f8bb3d..fae072637 100644 --- a/config/regionalroi/spb-2022-d1/settings.json +++ b/config/regionalroi/spb-2022-d1/settings.json @@ -1,5 +1,5 @@ { "type": "pcms", - "url": "regionalroi/spb-2022-d1/ru-olymp-spb-2022-standings-day1-groups.xml", + "url": "ru-olymp-spb-2022-standings-day1-groups.xml", "resultType": "IOI" } diff --git a/config/roi/2022/day1/settings.json b/config/roi/2022/day1/settings.json index 2cfa630a3..1cd99725d 100644 --- a/config/roi/2022/day1/settings.json +++ b/config/roi/2022/day1/settings.json @@ -2,6 +2,6 @@ "type": "pcms", "login": "admin", "password": "adm1n", - "url": "roi-2022-day1/standings-live-day1.xml", + "url": "standings-live-day1.xml", "resultType": "IOI" } diff --git a/config/roi/2023/day1/settings.json b/config/roi/2023/day1/settings.json index 91fabe8a6..3ff479cc8 100644 --- a/config/roi/2023/day1/settings.json +++ b/config/roi/2023/day1/settings.json @@ -1,6 +1,5 @@ { - "type":"pcms", - "#url":"http://192.168.90.248/standings/standings-live.xml", - "url":"roi/2023/day1/day1.xml", - "resultType":"IOI" + "type": "pcms", + "url": "day1.xml", + "resultType": "IOI" } \ No newline at end of file diff --git a/config/roi/2023/day2/settings.json b/config/roi/2023/day2/settings.json index 31379391e..366232dba 100644 --- a/config/roi/2023/day2/settings.json +++ b/config/roi/2023/day2/settings.json @@ -1,6 +1,5 @@ { "type": "pcms", - "#url": "http://192.168.90.248/standings/standings-live.xml", - "url": "roi/2023/day2/day2.xml", + "url": "day2.xml", "resultType": "IOI" } diff --git a/config/roi/2023/it-sequel/settings.json b/config/roi/2023/it-sequel/settings.json index f9c77a561..260d56f37 100644 --- a/config/roi/2023/it-sequel/settings.json +++ b/config/roi/2023/it-sequel/settings.json @@ -1,7 +1,6 @@ { "type": "pcms", - "#url": "http://192.168.90.248/standings/standings-live.xml", - "url": "roi/2023/it-sequel/it-sequel.xml", + "url": "it-sequel.xml", "resultType": "IOI" } diff --git a/config/roi/2023/test/settings.json b/config/roi/2023/test/settings.json index 9bb80f5d4..5e73d124e 100644 --- a/config/roi/2023/test/settings.json +++ b/config/roi/2023/test/settings.json @@ -1,5 +1,5 @@ { - "type":"pcms", - "url":"roi/2023/test/standings-live.xml", - "resultType":"IOI" + "type": "pcms", + "url": "standings-live.xml", + "resultType": "IOI" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0ccfa89d0..cb6e36a76 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,6 +20,7 @@ grpc-kotlin = "1.3.1" # https://github.com/grpc/grpc-kotlin dokka = "1.8.20" # https://github.com/Kotlin/dokka retrofit = "2.9.0" # https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit/2.9.0 json5 = "0.3.0" # https://github.com/xn32/json5k +kxs-ts-gen = "0.2.1" # https://github.com/adamko-dev/kotlinx-serialization-typescript-generator [libraries] @@ -74,6 +75,7 @@ grpc-gen-kotlin = { version.ref = "grpc-kotlin", group = "io.grpc", name = "prot protobuf = { version.ref = "protobuf", group = "com.google.protobuf", name = "protobuf-kotlin" } protoc = { version.ref = "protobuf", group = "com.google.protobuf", name = "protoc" } +kxs-ts-gen-core = { version.ref = "kxs-ts-gen" , group = "dev.adamko.kxstsgen", name = "kxs-ts-gen-core" } [plugins] diff --git a/schemas/advanced.schema.json b/schemas/advanced.schema.json index 183265bb6..f948cd358 100644 --- a/schemas/advanced.schema.json +++ b/schemas/advanced.schema.json @@ -1,822 +1,193 @@ { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/icpc/live-v3/blob/main/schemas/advanced.schema.json", - "title": "ICPC live advanced settings", - "type": "object", - "properties": { - "startTime": { - "type": "string" + "$ref": "#/$defs/ICPC live advanced settings", + "$defs": { + "Object": { + "type": "object", + "properties": { + "type": { + "const": "Object", + "default": "Object" + }, + "url": { + "type": "string" + }, + "isMedia": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "type", + "url" + ], + "title": "Object" }, - "freezeTimeSeconds": { - "type": "integer" + "Photo": { + "type": "object", + "properties": { + "type": { + "const": "Photo", + "default": "Photo" + }, + "url": { + "type": "string" + }, + "isMedia": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "type", + "url" + ], + "title": "Photo" }, - "holdTimeSeconds": { - "type": "integer" + "TaskStatus": { + "type": "object", + "properties": { + "type": { + "const": "TaskStatus", + "default": "TaskStatus" + }, + "teamId": { + "type": "number" + }, + "isMedia": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "type", + "teamId" + ], + "title": "TaskStatus" }, - "teamMediaTemplate": { + "Video": { + "type": "object", + "properties": { + "type": { + "const": "Video", + "default": "Video" + }, + "url": { + "type": "string" + }, + "isMedia": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "type", + "url" + ], + "title": "Video" + }, + "WebRTCGrabberConnection": { + "type": "object", + "properties": { + "type": { + "const": "WebRTCGrabberConnection", + "default": "WebRTCGrabberConnection" + }, + "url": { + "type": "string" + }, + "peerName": { + "type": "string" + }, + "streamType": { + "type": "string" + }, + "credential": { + "type": "string" + }, + "isMedia": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "type", + "url", + "peerName", + "streamType", + "credential" + ], + "title": "WebRTCGrabberConnection" + }, + "WebRTCProxyConnection": { + "type": "object", + "properties": { + "type": { + "const": "WebRTCProxyConnection", + "default": "WebRTCProxyConnection" + }, + "url": { + "type": "string" + }, + "audioUrl": { + "type": "string" + }, + "isMedia": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "type", + "url" + ], + "title": "WebRTCProxyConnection" + }, + "org.icpclive.api.MediaType?>": { + "oneOf": [ + { + "$ref": "#/$defs/Object" + }, + { + "$ref": "#/$defs/Photo" + }, + { + "$ref": "#/$defs/TaskStatus" + }, + { + "$ref": "#/$defs/Video" + }, + { + "$ref": "#/$defs/WebRTCGrabberConnection" + }, + { + "$ref": "#/$defs/WebRTCProxyConnection" + } + ] + }, + "kotlin.collections.LinkedHashMap?": { "type": "object", "properties": { "camera": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] + "$ref": "#/$defs/org.icpclive.api.MediaType?>" }, "screen": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] + "$ref": "#/$defs/org.icpclive.api.MediaType?>" }, "record": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] + "$ref": "#/$defs/org.icpclive.api.MediaType?>" }, "photo": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] + "$ref": "#/$defs/org.icpclive.api.MediaType?>" }, "reactionVideo": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] + "$ref": "#/$defs/org.icpclive.api.MediaType?>" }, "achievement": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] + "$ref": "#/$defs/org.icpclive.api.MediaType?>" } } }, - "teamOverrideTemplate": { + "org.icpclive.api.tunning.TeamOverrideTemplate?": { "type": "object", "properties": { "displayName": { @@ -826,1819 +197,289 @@ "type": "string" }, "medias": { - "type": "object", - "properties": { - "camera": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "screen": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "record": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "photo": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "reactionVideo": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "achievement": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - } - } + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" } }, "additionalProperties": false, "required": [ ] }, - "teamRegexes": { + "kotlin.collections.LinkedHashMap?": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + }, + "org.icpclive.api.tunning.TeamRegexOverrides?": { "type": "object", "properties": { "organizationRegex": { "type": "string" }, "customFields": { - "type": "object", - "patternProperties": { - ".*": { - "type": "string" - } - } + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" }, "groupRegex": { - "type": "object", - "patternProperties": { - ".*": { - "type": "string" - } - } + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" } }, "additionalProperties": false, "required": [ ] }, - "teamOverrides": { + "kotlin.collections.ArrayList?": { + "type": "array", + "items": { + "type": "string" + } + }, + "kotlin.collections.LinkedHashMap?": { "type": "object", "patternProperties": { ".*": { - "type": "object", - "properties": { - "fullName": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "groups": { - "type": "array", - "items": { - "type": "string" - } - }, - "organizationId": { - "type": "string" - }, - "hashTag": { - "type": "string" - }, - "medias": { - "type": "object", - "properties": { - "camera": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "screen": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "record": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "photo": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "reactionVideo": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - }, - "achievement": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "const": "Object" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Photo" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "TaskStatus" - }, - "teamId": { - "type": "integer" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "teamId" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "Video" - }, - "url": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCGrabberConnection" - }, - "url": { - "type": "string" - }, - "peerName": { - "type": "string" - }, - "streamType": { - "type": "string" - }, - "credential": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url", - "peerName", - "streamType", - "credential" - ] - }, - { - "type": "object", - "properties": { - "type": { - "const": "WebRTCProxyConnection" - }, - "url": { - "type": "string" - }, - "audioUrl": { - "type": "string" - }, - "isMedia": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "type", - "url" - ] - } - ] - } - } - }, - "customFields": { - "type": "object", - "patternProperties": { - ".*": { - "type": "string" - } - } - }, - "isHidden": { - "type": "boolean" - }, - "isOutOfContest": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "type": "string" } } }, - "groupOverrides": { + "org.icpclive.api.tunning.TeamInfoOverride": { + "type": "object", + "properties": { + "fullName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "groups": { + "$ref": "#/$defs/kotlin.collections.ArrayList?" + }, + "organizationId": { + "type": "string" + }, + "hashTag": { + "type": "string" + }, + "medias": { + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" + }, + "customFields": { + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" + }, + "isHidden": { + "type": "boolean" + }, + "isOutOfContest": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + ] + }, + "kotlin.collections.LinkedHashMap?": { + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/$defs/org.icpclive.api.tunning.TeamInfoOverride" + } + } + }, + "org.icpclive.api.tunning.GroupInfoOverride": { + "type": "object", + "properties": { + "isHidden": { + "type": "boolean" + }, + "isOutOfContest": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + ] + }, + "kotlin.collections.LinkedHashMap?": { "type": "object", "patternProperties": { ".*": { - "type": "object", - "properties": { - "isHidden": { - "type": "boolean" - }, - "isOutOfContest": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.api.tunning.GroupInfoOverride" } } }, - "organizationOverrides": { + "org.icpclive.api.tunning.OrganizationInfoOverride": { + "type": "object", + "properties": { + "displayName": { + "type": "string" + }, + "fullName": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + ] + }, + "kotlin.collections.LinkedHashMap?": { "type": "object", "patternProperties": { ".*": { - "type": "object", - "properties": { - "displayName": { - "type": "string" - }, - "fullName": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.api.tunning.OrganizationInfoOverride" } } }, - "problemOverrides": { + "org.icpclive.api.ScoreMergeMode?": { + "enum": [ + "MAX_PER_GROUP", + "MAX_TOTAL", + "LAST", + "LAST_OK", + "SUM" + ] + }, + "org.icpclive.api.tunning.ProblemInfoOverride": { + "type": "object", + "properties": { + "displayName": { + "type": "string" + }, + "fullName": { + "type": "string" + }, + "color": { + "type": "string" + }, + "ordinal": { + "type": "number" + }, + "minScore": { + "type": "number" + }, + "maxScore": { + "type": "number" + }, + "scoreMergeMode": { + "$ref": "#/$defs/org.icpclive.api.ScoreMergeMode?" + } + }, + "additionalProperties": false, + "required": [ + ] + }, + "kotlin.collections.LinkedHashMap?": { "type": "object", "patternProperties": { ".*": { - "type": "object", - "properties": { - "displayName": { - "type": "string" - }, - "fullName": { - "type": "string" - }, - "color": { - "type": "string" - }, - "ordinal": { - "type": "integer" - }, - "minScore": { - "type": "number" - }, - "maxScore": { - "type": "number" - }, - "scoreMergeMode": { - "enum": [ - "MAX_PER_GROUP", - "MAX_TOTAL", - "LAST", - "LAST_OK", - "SUM" - ] - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.api.tunning.ProblemInfoOverride" + } + } + }, + "org.icpclive.api.MedalTiebreakMode": { + "enum": [ + "NONE", + "ALL" + ] + }, + "org.icpclive.api.MedalType": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "count": { + "type": "number" + }, + "minScore": { + "type": "number" + }, + "tiebreakMode": { + "$ref": "#/$defs/org.icpclive.api.MedalTiebreakMode" } + }, + "additionalProperties": false, + "required": [ + "name", + "count" + ] + }, + "kotlin.collections.ArrayList?": { + "type": "array", + "items": { + "$ref": "#/$defs/org.icpclive.api.MedalType" } }, - "scoreboardOverrides": { + "org.icpclive.api.PenaltyRoundingMode?": { + "enum": [ + "each_submission_down_to_minute", + "sum_down_to_minute", + "sum_in_seconds", + "last", + "zero" + ] + }, + "org.icpclive.api.tunning.RankingSettings?": { "type": "object", "properties": { "medals": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "count": { - "type": "integer" - }, - "minScore": { - "type": "number" - }, - "tiebreakMode": { - "enum": [ - "NONE", - "ALL" - ] - } - }, - "additionalProperties": false, - "required": [ - "name", - "count" - ] - } + "$ref": "#/$defs/kotlin.collections.ArrayList?" }, "penaltyPerWrongAttempt": { - "type": "integer" + "type": "number" }, "showTeamsWithoutSubmissions": { "type": "boolean" }, "penaltyRoundingMode": { - "enum": [ - "each_submission_down_to_minute", - "sum_down_to_minute", - "sum_in_seconds", - "last", - "zero" - ] + "$ref": "#/$defs/org.icpclive.api.PenaltyRoundingMode?" } }, "additionalProperties": false, "required": [ ] + }, + "ICPC live advanced settings": { + "type": "object", + "properties": { + "startTime": { + "type": "string" + }, + "freezeTimeSeconds": { + "type": "number" + }, + "holdTimeSeconds": { + "type": "number" + }, + "teamMediaTemplate": { + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" + }, + "teamOverrideTemplate": { + "$ref": "#/$defs/org.icpclive.api.tunning.TeamOverrideTemplate?" + }, + "teamRegexes": { + "$ref": "#/$defs/org.icpclive.api.tunning.TeamRegexOverrides?" + }, + "teamOverrides": { + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" + }, + "groupOverrides": { + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" + }, + "organizationOverrides": { + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" + }, + "problemOverrides": { + "$ref": "#/$defs/kotlin.collections.LinkedHashMap?" + }, + "scoreboardOverrides": { + "$ref": "#/$defs/org.icpclive.api.tunning.RankingSettings?" + } + }, + "additionalProperties": false, + "required": [ + ], + "title": "ICPC live advanced settings" } - }, - "additionalProperties": false, - "required": [ - ] + } } diff --git a/schemas/settings.schema.json b/schemas/settings.schema.json index 2568492d7..5f9d34520 100644 --- a/schemas/settings.schema.json +++ b/schemas/settings.schema.json @@ -1,122 +1,81 @@ { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/icpc/live-v3/blob/main/schemas/settings.schema.json", - "title": "ICPC live settings", - "oneOf": [ - { + "$ref": "#/$defs/ICPC live settings", + "$defs": { + "org.icpclive.cds.settings.EmulationSettings?": { "type": "object", "properties": { - "type": { - "const": "atcoder" - }, - "contestId": { - "type": "string" - }, - "sessionCookie": { - "type": "string" + "speed": { + "type": "number" }, "startTime": { "type": "string" - }, - "contestLengthSeconds": { - "type": "integer" - }, - "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] - }, - "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] } }, "additionalProperties": false, "required": [ - "type", - "contestId", - "sessionCookie", - "startTime", - "contestLengthSeconds" + "speed", + "startTime" + ] + }, + "org.icpclive.cds.settings.NetworkSettings?": { + "type": "object", + "properties": { + "allowUnsecureConnections": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ ] }, - { + "atcoder": { "type": "object", "properties": { "type": { - "const": "cf" + "const": "atcoder", + "default": "atcoder" }, "contestId": { - "type": "integer" + "type": "string" }, - "apiKey": { + "sessionCookie": { "type": "string" }, - "apiSecret": { + "startTime": { "type": "string" }, - "asManager": { - "type": "boolean" + "contestLengthSeconds": { + "type": "number" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, "required": [ "type", "contestId", - "apiKey", - "apiSecret" + "sessionCookie", + "startTime", + "contestLengthSeconds" + ], + "title": "atcoder" + }, + "org.icpclive.api.ContestResultType": { + "enum": [ + "ICPC", + "IOI" ] }, - { + "cats": { "type": "object", "properties": { "type": { - "const": "cats" + "const": "cats", + "default": "cats" }, "login": { "type": "string" @@ -131,40 +90,16 @@ "type": "string" }, "resultType": { - "enum": [ - "ICPC", - "IOI" - ] + "$ref": "#/$defs/org.icpclive.api.ContestResultType" }, "cid": { "type": "string" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, @@ -174,17 +109,59 @@ "password", "url", "cid" - ] + ], + "title": "cats" }, - { + "cf": { "type": "object", "properties": { "type": { - "const": "clics" + "const": "cf", + "default": "cf" + }, + "contestId": { + "type": "number" + }, + "apiKey": { + "type": "string" + }, + "apiSecret": { + "type": "string" }, + "asManager": { + "type": "boolean" + }, + "emulation": { + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" + }, + "network": { + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" + } + }, + "additionalProperties": false, + "required": [ + "type", + "contestId", + "apiKey", + "apiSecret" + ], + "title": "cf" + }, + "org.icpclive.cds.settings.ClicsSettings.FeedVersion": { + "enum": [ + "2020_03", + "2022_07" + ] + }, + "org.icpclive.cds.settings.ClicsFeed,kotlin.String,kotlinx.serialization.ContextualSerializer?,kotlinx.serialization.ContextualSerializer?,kotlin.String,kotlin.String?,org.icpclive.cds.settings.ClicsSettings.FeedVersion>": { + "type": "object", + "properties": { "url": { "type": "string" }, + "contestId": { + "type": "string" + }, "login": { "type": "string" }, @@ -194,84 +171,64 @@ "eventFeedName": { "type": "string" }, + "eventFeedPath": { + "type": "string" + }, "feedVersion": { - "enum": [ - "2020_03", - "2022_07" - ] - }, - "additionalFeed": { - "type": "object", - "properties": { - "url": { - "type": "string" - }, - "login": { - "type": "string" - }, - "password": { - "type": "string" - }, - "eventFeedName": { - "type": "string" - }, - "feedVersion": { - "enum": [ - "2020_03", - "2022_07" - ] - } - }, - "additionalProperties": false, - "required": [ - "url" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.ClicsSettings.FeedVersion" + } + }, + "additionalProperties": false, + "required": [ + "url", + "contestId" + ] + }, + "kotlin.collections.ArrayList": { + "type": "array", + "items": { + "$ref": "#/$defs/org.icpclive.cds.settings.ClicsFeed,kotlin.String,kotlinx.serialization.ContextualSerializer?,kotlinx.serialization.ContextualSerializer?,kotlin.String,kotlin.String?,org.icpclive.cds.settings.ClicsSettings.FeedVersion>" + } + }, + "clics": { + "type": "object", + "properties": { + "type": { + "const": "clics", + "default": "clics" + }, + "feeds": { + "$ref": "#/$defs/kotlin.collections.ArrayList" }, "useTeamNames": { "type": "boolean" }, - "mediaBaseUrl": { - "type": "string" - }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, "required": [ "type", - "url" - ] + "feeds" + ], + "title": "clics" + }, + "kotlin.collections.ArrayList": { + "type": "array", + "items": { + "type": "string" + } }, - { + "cms": { "type": "object", "properties": { "type": { - "const": "cms" + "const": "cms", + "default": "cms" }, "url": { "type": "string" @@ -280,37 +237,13 @@ "type": "string" }, "otherContests": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/$defs/kotlin.collections.ArrayList" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" } }, "additionalProperties": false, @@ -319,19 +252,21 @@ "url", "activeContest", "otherContests" - ] + ], + "title": "cms" }, - { + "codedrills": { "type": "object", "properties": { "type": { - "const": "codedrills" + "const": "codedrills", + "default": "codedrills" }, "url": { "type": "string" }, "port": { - "type": "integer" + "type": "number" }, "contestId": { "type": "string" @@ -340,31 +275,10 @@ "type": "string" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, @@ -374,65 +288,45 @@ "port", "contestId", "authKey" - ] + ], + "title": "codedrills" }, - { + "ejudge": { "type": "object", "properties": { "type": { - "const": "ejudge" + "const": "ejudge", + "default": "ejudge" }, "url": { "type": "string" }, "resultType": { - "enum": [ - "ICPC", - "IOI" - ] + "$ref": "#/$defs/org.icpclive.api.ContestResultType" }, "timeZone": { "type": "string" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, "required": [ "type", "url" - ] + ], + "title": "ejudge" }, - { + "krsu": { "type": "object", "properties": { "type": { - "const": "krsu" + "const": "krsu", + "default": "krsu" }, "submissionsUrl": { "type": "string" @@ -444,31 +338,10 @@ "type": "string" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, @@ -476,52 +349,35 @@ "type", "submissionsUrl", "contestUrl" - ] + ], + "title": "krsu" }, - { + "noop": { "type": "object", "properties": { "type": { - "const": "noop" + "const": "noop", + "default": "noop" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, "required": [ "type" - ] + ], + "title": "noop" }, - { + "pcms": { "type": "object", "properties": { "type": { - "const": "pcms" + "const": "pcms", + "default": "pcms" }, "url": { "type": "string" @@ -536,50 +392,28 @@ "type": "string" }, "resultType": { - "enum": [ - "ICPC", - "IOI" - ] + "$ref": "#/$defs/org.icpclive.api.ContestResultType" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, "required": [ "type", "url" - ] + ], + "title": "pcms" }, - { + "testsys": { "type": "object", "properties": { "type": { - "const": "testsys" + "const": "testsys", + "default": "testsys" }, "url": { "type": "string" @@ -588,44 +422,25 @@ "type": "string" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, "required": [ "type", "url" - ] + ], + "title": "testsys" }, - { + "yandex": { "type": "object", "properties": { "type": { - "const": "yandex" + "const": "yandex", + "default": "yandex" }, "apiKey": { "type": "string" @@ -634,40 +449,16 @@ "type": "string" }, "contestId": { - "type": "integer" + "type": "number" }, "resultType": { - "enum": [ - "ICPC", - "IOI" - ] + "$ref": "#/$defs/org.icpclive.api.ContestResultType" }, "emulation": { - "type": "object", - "properties": { - "speed": { - "type": "number" - }, - "startTime": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "speed", - "startTime" - ] + "$ref": "#/$defs/org.icpclive.cds.settings.EmulationSettings?" }, "network": { - "type": "object", - "properties": { - "allowUnsecureConnections": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - ] + "$ref": "#/$defs/org.icpclive.cds.settings.NetworkSettings?" } }, "additionalProperties": false, @@ -676,7 +467,49 @@ "apiKey", "loginRegex", "contestId" - ] + ], + "title": "yandex" + }, + "ICPC live settings": { + "oneOf": [ + { + "$ref": "#/$defs/atcoder" + }, + { + "$ref": "#/$defs/cats" + }, + { + "$ref": "#/$defs/cf" + }, + { + "$ref": "#/$defs/clics" + }, + { + "$ref": "#/$defs/cms" + }, + { + "$ref": "#/$defs/codedrills" + }, + { + "$ref": "#/$defs/ejudge" + }, + { + "$ref": "#/$defs/krsu" + }, + { + "$ref": "#/$defs/noop" + }, + { + "$ref": "#/$defs/pcms" + }, + { + "$ref": "#/$defs/testsys" + }, + { + "$ref": "#/$defs/yandex" + } + ], + "title": "ICPC live settings" } - ] + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 79f6c4458..414a24f4c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,6 +18,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") include( ":backend", + ":backend-api", ":cds", ":cds-converter", ":clics-api", @@ -29,6 +30,7 @@ include( ":faker" ) project(":backend").projectDir = file("src/backend") +project(":backend-api").projectDir = file("src/backend-api") project(":cds").projectDir = file("src/cds") project(":cds-converter").projectDir = file("src/cds-converter") project(":clics-api").projectDir = file("src/clics-api") diff --git a/src/backend-api/build.gradle.kts b/src/backend-api/build.gradle.kts new file mode 100644 index 000000000..d17a6f36f --- /dev/null +++ b/src/backend-api/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + `java-library` + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.kotlin.serialization) +} + +dependencies { + api(libs.kotlinx.serialization.json) + api(libs.kotlinx.datetime) + api(projects.common) + api(projects.cds) + testImplementation(libs.kotlin.junit) +} \ No newline at end of file diff --git a/src/backend/src/main/kotlin/org/icpclive/api/AdminUser.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/AdminUser.kt similarity index 100% rename from src/backend/src/main/kotlin/org/icpclive/api/AdminUser.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/AdminUser.kt diff --git a/src/backend/src/main/kotlin/org/icpclive/api/Event.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Event.kt similarity index 100% rename from src/backend/src/main/kotlin/org/icpclive/api/Event.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/Event.kt diff --git a/src/backend/src/main/kotlin/org/icpclive/api/Objects.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Objects.kt similarity index 100% rename from src/backend/src/main/kotlin/org/icpclive/api/Objects.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/Objects.kt diff --git a/src/backend/src/main/kotlin/org/icpclive/api/Settings.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt similarity index 93% rename from src/backend/src/main/kotlin/org/icpclive/api/Settings.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt index b59a64afb..08438933a 100644 --- a/src/backend/src/main/kotlin/org/icpclive/api/Settings.kt +++ b/src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt @@ -4,7 +4,7 @@ package org.icpclive.api import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import org.icpclive.admin.ApiActionException +import java.lang.IllegalArgumentException interface ObjectSettings @@ -81,13 +81,7 @@ data class TeamLocatorExternalCircleSettings( val radius: Int, val teamId: Int? = null, val cdsTeamId: String? = null, -) { - init { - if ((teamId == null) == (cdsTeamId == null)) { - throw ApiActionException("Only one of of teamId and cdsTeamsId can be specified") - } - } -} +) @Serializable data class ExternalTeamLocatorSettings( diff --git a/src/backend/src/main/kotlin/org/icpclive/api/Social.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Social.kt similarity index 100% rename from src/backend/src/main/kotlin/org/icpclive/api/Social.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/Social.kt diff --git a/src/backend/src/main/kotlin/org/icpclive/api/SpotlightTeam.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/SpotlightTeam.kt similarity index 88% rename from src/backend/src/main/kotlin/org/icpclive/api/SpotlightTeam.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/SpotlightTeam.kt index 48adc6a97..65e22fe3f 100644 --- a/src/backend/src/main/kotlin/org/icpclive/api/SpotlightTeam.kt +++ b/src/backend-api/src/main/kotlin/org/icpclive/api/SpotlightTeam.kt @@ -1,7 +1,6 @@ package org.icpclive.api import kotlinx.serialization.Serializable -import org.icpclive.service.TeamState import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -10,9 +9,7 @@ class RunCause(val runId: Int) : KeyTeamCause() object ScoreSumCause : KeyTeamCause() data class KeyTeam(val teamId: Int, val cause: KeyTeamCause) -data class CurrentTeamState(val teamId: Int, val score: Double) { - constructor(state: TeamState) : this(state.teamId, state.score) -} +data class CurrentTeamState(val teamId: Int, val score: Double) @Serializable data class InterestingTeam(val teamId: Int, val teamName: String, val score: Double) diff --git a/src/backend/src/main/kotlin/org/icpclive/api/Statistics.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Statistics.kt similarity index 100% rename from src/backend/src/main/kotlin/org/icpclive/api/Statistics.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/Statistics.kt diff --git a/src/backend/src/main/kotlin/org/icpclive/api/Ticker.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Ticker.kt similarity index 100% rename from src/backend/src/main/kotlin/org/icpclive/api/Ticker.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/Ticker.kt diff --git a/src/backend/src/main/kotlin/org/icpclive/api/Widgets.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Widgets.kt similarity index 97% rename from src/backend/src/main/kotlin/org/icpclive/api/Widgets.kt rename to src/backend-api/src/main/kotlin/org/icpclive/api/Widgets.kt index 5b8b4a725..10c5d9272 100644 --- a/src/backend/src/main/kotlin/org/icpclive/api/Widgets.kt +++ b/src/backend-api/src/main/kotlin/org/icpclive/api/Widgets.kt @@ -4,10 +4,11 @@ package org.icpclive.api import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import org.icpclive.Config import kotlin.random.Random import kotlin.random.nextUInt +var defaultWidgetPositions: Map = emptyMap() + fun generateId(widgetPrefix: String): String = "$widgetPrefix-${Random.nextUInt()}" @Serializable @@ -19,7 +20,7 @@ class LocationRectangle( ) fun getLocationOrDefault(widgetPrefix: String, defaultLocationRectangle: LocationRectangle) = - Config.widgetPositions.getOrDefault(widgetPrefix, defaultLocationRectangle) + defaultWidgetPositions.getOrDefault(widgetPrefix, defaultLocationRectangle) @Serializable sealed class Widget( diff --git a/src/backend/build.gradle.kts b/src/backend/build.gradle.kts index c69e96dc7..9b98913d2 100644 --- a/src/backend/build.gradle.kts +++ b/src/backend/build.gradle.kts @@ -54,6 +54,7 @@ tasks { dependencies { implementation(projects.cds) implementation(projects.common) + implementation(projects.backendApi) implementation(libs.cli) implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.ktor.server.auth) diff --git a/src/backend/src/main/kotlin/org/icpclive/Application.kt b/src/backend/src/main/kotlin/org/icpclive/Application.kt index 01f9be8b2..4d5e5148a 100644 --- a/src/backend/src/main/kotlin/org/icpclive/Application.kt +++ b/src/backend/src/main/kotlin/org/icpclive/Application.kt @@ -133,8 +133,8 @@ fun Application.module() { .stateIn(this) DataBus.advancedPropertiesFlow.completeOrThrow(advancedPropertiesFlow) - val loader = parseFileToCdsSettings(path) - .toFlow(config.creds) + val loader = parseFileToCdsSettings(path, config.creds) + .toFlow() .applyAdvancedProperties(advancedPropertiesFlow) .contestState() .filterUseless() diff --git a/src/backend/src/main/kotlin/org/icpclive/Config.kt b/src/backend/src/main/kotlin/org/icpclive/Config.kt index ca97ba76c..70798fda4 100644 --- a/src/backend/src/main/kotlin/org/icpclive/Config.kt +++ b/src/backend/src/main/kotlin/org/icpclive/Config.kt @@ -8,6 +8,7 @@ import com.github.ajalt.clikt.parameters.types.* import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import org.icpclive.api.LocationRectangle +import org.icpclive.api.defaultWidgetPositions object Config : CliktCommand(name = "java -jar live-v3.jar", printHelpOnEmptyArgs = true) { val configDirectory by option( @@ -50,12 +51,14 @@ object Config : CliktCommand(name = "java -jar live-v3.jar", printHelpOnEmptyArg path.toFile().inputStream().use { Json.decodeFromStream>(it) } }.default(emptyMap(), "none") + val analyticsTemplatesFile by option( "--analytics-template", help = "File with localization of analytics messages" ).path(canBeFile = true, canBeDir = false, mustExist = true) override fun run() { + defaultWidgetPositions = widgetPositions presetsDirectory.toFile().mkdirs() mediaDirectory.toFile().mkdirs() io.ktor.server.netty.EngineMain.main((listOf("-port=$port") + ktorArgs).toTypedArray()) diff --git a/src/backend/src/main/kotlin/org/icpclive/controllers/LocatorWidgetController.kt b/src/backend/src/main/kotlin/org/icpclive/controllers/LocatorWidgetController.kt index 36c8631ae..ad02b248d 100644 --- a/src/backend/src/main/kotlin/org/icpclive/controllers/LocatorWidgetController.kt +++ b/src/backend/src/main/kotlin/org/icpclive/controllers/LocatorWidgetController.kt @@ -15,7 +15,10 @@ class LocatorWidgetController(manager: Manager) : override suspend fun checkSettings(settings: ExternalTeamLocatorSettings) { val info = DataBus.contestInfoFlow.await().first() - settings.circles.forEach { it.getTeam(info) ?: throw ApiActionException("No team for circle $it") } + settings.circles.forEach { + if ((it.teamId == null) == (it.cdsTeamId == null)) throw ApiActionException("Only one of of teamId and cdsTeamsId can be specified") + it.getTeam(info) ?: throw ApiActionException("No team for circle $it") + } } override suspend fun constructWidget(settings: ExternalTeamLocatorSettings): TeamLocatorWidget { diff --git a/src/backend/src/main/kotlin/org/icpclive/service/TeamSpotlightService.kt b/src/backend/src/main/kotlin/org/icpclive/service/TeamSpotlightService.kt index bcca59716..c6d7e3a6e 100644 --- a/src/backend/src/main/kotlin/org/icpclive/service/TeamSpotlightService.kt +++ b/src/backend/src/main/kotlin/org/icpclive/service/TeamSpotlightService.kt @@ -105,7 +105,7 @@ class TeamSpotlightService( continue } emit(KeyTeam(element.teamId, element.caused)) - val teams = mutex.withLock { queue.map(::CurrentTeamState) } + val teams = mutex.withLock { queue.map { CurrentTeamState(it.teamId, it.score) } } teamInteresting?.emit(teams) } } @@ -113,7 +113,7 @@ class TeamSpotlightService( private suspend fun TeamState.addAccent(accent: TeamAccent) { addAccent(accent, settings) - teamInteresting?.emit(queue.map(::CurrentTeamState)) + teamInteresting?.emit(queue.map { CurrentTeamState(it.teamId, it.score) } ) } suspend fun run( diff --git a/src/cds-converter/src/main/kotlin/Application.kt b/src/cds-converter/src/main/kotlin/Application.kt index 4e0ce68d1..b27f998ee 100644 --- a/src/cds-converter/src/main/kotlin/Application.kt +++ b/src/cds-converter/src/main/kotlin/Application.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream -import org.icpclive.api.ContestStatus import org.icpclive.api.tunning.AdvancedProperties import org.icpclive.cds.ContestUpdate import org.icpclive.cds.adapters.* @@ -180,8 +179,8 @@ private fun getFlow(advancedProperties: Flow, log: Logger) : val creds: Map = CommonOptions.credsFile?.let { Json.decodeFromStream(it.toFile().inputStream()) } ?: emptyMap() - return parseFileToCdsSettings(path) - .toFlow(creds) + return parseFileToCdsSettings(path, creds) + .toFlow() .applyAdvancedProperties(advancedProperties) .contestState() .filterUseless() diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/atcoder/AtcoderDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/atcoder/AtcoderDataSource.kt index 4fbf741c6..4389546c5 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/atcoder/AtcoderDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/atcoder/AtcoderDataSource.kt @@ -43,10 +43,10 @@ internal class ContestData( val StandingsData: List ) -internal class AtcoderDataSource(val settings: AtcoderSettings, creds: Map) : FullReloadContestDataSource(5.seconds) { +internal class AtcoderDataSource(val settings: AtcoderSettings) : FullReloadContestDataSource(5.seconds) { val teamIds = Enumerator() val problemIds = Enumerator() - private val loader = jsonLoader(settings.network, ClientAuth.CookieAuth("REVEL_SESSION", settings.sessionCookie.get(creds))) { "https://atcoder.jp/contests/${settings.contestId}/standings/json" } + private val loader = jsonLoader(settings.network, ClientAuth.CookieAuth("REVEL_SESSION", settings.sessionCookie.value)) { "https://atcoder.jp/contests/${settings.contestId}/standings/json" } var submissionId: Int = 1 val runs = mutableMapOf, List>() diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/cats/CATSDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/cats/CATSDataSource.kt index fbad739c1..0ae03b720 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/cats/CATSDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/cats/CATSDataSource.kt @@ -38,9 +38,9 @@ private object SubmissionTimeSerializer : KSerializer { } } -internal class CATSDataSource(val settings: CatsSettings, creds: Map) : FullReloadContestDataSource(5.seconds) { - private val login = settings.login.get(creds) - private val password = settings.password.get(creds) +internal class CATSDataSource(val settings: CatsSettings) : FullReloadContestDataSource(5.seconds) { + private val login = settings.login.value + private val password = settings.password.value private var sid: String? = null diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsDataSource.kt index 840a43a90..739ba26f1 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsDataSource.kt @@ -5,37 +5,31 @@ import kotlinx.coroutines.flow.* import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule import org.icpclive.api.* import org.icpclive.cds.* -import org.icpclive.clics.Event import org.icpclive.clics.Event.* import org.icpclive.cds.common.* import org.icpclive.cds.settings.* -import org.icpclive.util.getLogger -import org.icpclive.util.logAndRetryWithDelay +import org.icpclive.clics.* +import org.icpclive.util.* import kotlin.time.Duration.Companion.seconds -private class ParsedClicsLoaderSettings(settings: ClicsLoaderSettings, creds: Map) { - private val url = settings.url - +private class ParsedClicsLoaderSettings(settings: ClicsFeed) { val auth = ClientAuth.BasicOrNull( - settings.login?.get(creds), - settings.password?.get(creds) + settings.login?.value, + settings.password?.value ) - val eventFeedUrl = apiRequestUrl(settings.eventFeedName) - - private fun apiRequestUrl(method: String) = "$url/$method" - + val baseUrl = settings.url + val eventFeedUrl = "$baseUrl/${settings.eventFeedPath ?: "contests/${settings.contestId}"}/${settings.eventFeedName}" val feedVersion = settings.feedVersion } -internal class ClicsDataSource(val settings: ClicsSettings, creds: Map) : ContestDataSource { - private val mainLoaderSettings = ParsedClicsLoaderSettings(settings.mainFeed, creds) - private val additionalLoaderSettings = settings.additionalFeed?.let { ParsedClicsLoaderSettings(it, creds) } +internal class ClicsDataSource(val settings: ClicsSettings) : ContestDataSource { + private val feeds = settings.feeds.map { ParsedClicsLoaderSettings(it) } private val model = ClicsModel( - settings.useTeamNames, - settings.mediaBaseUrl + settings.useTeamNames ) val Event.isFinalEvent get() = this is StateEvent && data?.end_of_updates != null @@ -45,8 +39,7 @@ internal class ClicsDataSource(val settings: ClicsSettings, creds: Map Unit, onComment: suspend (AnalyticsCommentaryEvent) -> Unit ) { - val eventsLoader = getEventFeedLoader(mainLoaderSettings, settings.network) - val additionalEventsLoader = additionalLoaderSettings?.let { getEventFeedLoader(it, settings.network) } + val loaders = feeds.map { getEventFeedLoader(it, settings.network) } fun priority(event: UpdateContestEvent) = when (event) { is ContestEvent -> 0 @@ -144,7 +137,8 @@ internal class ClicsDataSource(val settings: ClicsSettings, creds: Map() - merge(eventsLoader, additionalEventsLoader ?: emptyFlow()) + loaders + .merge() .logAndRetryWithDelay(5.seconds) { logger.error("Exception caught in CLICS loader. Will restart in 5 seconds.", it) preloadFinished = false @@ -180,6 +174,14 @@ internal class ClicsDataSource(val settings: ClicsSettings, creds: Map + if (it.href.startsWith("http://") || it.href.startsWith("https://")) + it + else + it.copy(href = "${settings.baseUrl}/${it.href}") + }) + } } while (true) { diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsModel.kt b/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsModel.kt index 9483ad67d..952c30845 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsModel.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/clics/ClicsModel.kt @@ -12,8 +12,7 @@ import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes internal class ClicsModel( - private val addTeamNames: Boolean, - private val mediaBaseUrl: String + private val addTeamNames: Boolean ) { private val judgementTypes = mutableMapOf() private val problems = mutableMapOf() @@ -26,13 +25,13 @@ internal class ClicsModel( private val judgements = mutableMapOf() private val groups = mutableMapOf() - var startTime: Instant? = null - var contestLength = 5.hours - var freezeTime = 4.hours - var status = ContestStatus.BEFORE - var penaltyPerWrongAttempt = 20.minutes - var holdBeforeStartTime: Duration? = null - var name: String = "" + private var startTime: Instant? = null + private var contestLength = 5.hours + private var freezeTime = 4.hours + private var status = ContestStatus.BEFORE + private var penaltyPerWrongAttempt = 20.minutes + private var holdBeforeStartTime: Duration? = null + private var name: String = "" fun getAllRuns() = submissions.values.map { it.toApi() } @@ -42,18 +41,12 @@ internal class ClicsModel( else -> org } - private fun String.maybeRelative() = when { - startsWith("http://") -> this - startsWith("https://") -> this - else -> "$mediaBaseUrl/$this" - } - private fun Media.mediaType(): MediaType? { if (mime.startsWith("image")) { - return MediaType.Photo(href.maybeRelative()) + return MediaType.Photo(href) } if (mime.startsWith("video")) { - return MediaType.Video(href.maybeRelative()) + return MediaType.Video(href) } return null } @@ -146,7 +139,7 @@ internal class ClicsModel( id = organization.id, name = organization.name, formalName = organization.formal_name ?: organization.name, - logo = organization.logo.lastOrNull()?.href?.maybeRelative(), + logo = organization.logo.lastOrNull()?.href?.let { it }, hashtag = organization.twitter_hashtag, ) } diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/cms/CmsDataSoruce.kt b/src/cds/src/main/kotlin/org/icpclive/cds/cms/CmsDataSoruce.kt index b75c146ff..043acade8 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/cms/CmsDataSoruce.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/cms/CmsDataSoruce.kt @@ -8,6 +8,7 @@ import org.icpclive.cds.common.jsonLoader import org.icpclive.cds.settings.CmsSettings import org.icpclive.util.Enumerator import kotlin.time.Duration +import kotlin.time.Duration.Companion.INFINITE import kotlin.time.Duration.Companion.seconds internal class CmsDataSource(val settings: CmsSettings) : FullReloadContestDataSource(5.seconds) { @@ -89,7 +90,7 @@ internal class CmsDataSource(val settings: CmsSettings) : FullReloadContestDataS resultType = ContestResultType.IOI, startTime = mainContest.begin, contestLength = mainContest.end - mainContest.begin, - freezeTime = mainContest.end - mainContest.begin, + freezeTime = INFINITE, problemList = problems, teamList = teams, groupList = emptyList(), @@ -111,7 +112,8 @@ internal class CmsDataSource(val settings: CmsSettings) : FullReloadContestDataS }.associateBy { it.id }.toMutableMap() subchangesLoader.load().entries.sortedBy { it.value.time }.forEach {(_, it) -> val r = submissions[submissionId[it.submission]] ?: return@forEach - submissions[r.id] = r.copy(result = IOIRunResult(it.extra.map { it.toDouble() })) + val scores = if (it.extra.isEmpty()) listOf(it.score) else it.extra.map { it.toDouble() } + submissions[r.id] = r.copy(result = IOIRunResult(scores)) } return ContestParseResult(info, submissions.values.sortedBy { it.id }, emptyList()) } diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/codedrills/CodeDrillsDataSoruce.kt b/src/cds/src/main/kotlin/org/icpclive/cds/codedrills/CodeDrillsDataSoruce.kt index 6808b4dc4..45ea4ee68 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/codedrills/CodeDrillsDataSoruce.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/codedrills/CodeDrillsDataSoruce.kt @@ -57,11 +57,12 @@ internal class CodeDrillsClient(url: String, port: Int, authKey: String) : AutoC } -internal class CodeDrillsDataSource(val settings: CodeDrillsSettings, creds: Map) : FullReloadContestDataSource(5.seconds) { +internal class CodeDrillsDataSource(val settings: CodeDrillsSettings) : FullReloadContestDataSource(5.seconds) { val client = CodeDrillsClient( - settings.url, + settings. + url, settings.port, - settings.authKey.get(creds) + settings.authKey.value, ) private fun SubmissionVerdict.toInfoVerdict() = when (this) { diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/codeforces/CFDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/codeforces/CFDataSource.kt index 197451714..812141682 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/codeforces/CFDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/codeforces/CFDataSource.kt @@ -13,10 +13,10 @@ import java.util.* import kotlin.random.Random import kotlin.time.Duration.Companion.seconds -internal class CFDataSource(val settings: CFSettings, creds: Map) : FullReloadContestDataSource(5.seconds) { +internal class CFDataSource(val settings: CFSettings) : FullReloadContestDataSource(5.seconds) { private val contestInfo = CFContestInfo() - private val apiKey = settings.apiKey.get(creds) - private val apiSecret = settings.apiSecret.get(creds) + private val apiKey = settings.apiKey.value + private val apiSecret = settings.apiSecret.value private fun apiRequestUrl( method: String, diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/ejudge/EjudgeDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/ejudge/EjudgeDataSource.kt index a499a7e9b..76f77f691 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/ejudge/EjudgeDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/ejudge/EjudgeDataSource.kt @@ -154,5 +154,5 @@ internal class EjudgeDataSource(val settings: EjudgeSettings) : FullReloadContes ) } - private val xmlLoader = xmlLoader(networkSettings = settings.network) { settings.url } + private val xmlLoader = xmlLoader(networkSettings = settings.network) { settings.url.value } } diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/krsu/KRSUDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/krsu/KRSUDataSource.kt index 209eef59a..2b61f9f31 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/krsu/KRSUDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/krsu/KRSUDataSource.kt @@ -81,8 +81,8 @@ internal class KRSUDataSource(val settings: KRSUSettings) : FullReloadContestDat ) } - private val submissionsLoader = jsonLoader>(networkSettings = settings.network) { settings.submissionsUrl } - private val contestInfoLoader = jsonLoader(networkSettings = settings.network) { settings.contestUrl } + private val submissionsLoader = jsonLoader>(networkSettings = settings.network) { settings.submissionsUrl.value } + private val contestInfoLoader = jsonLoader(networkSettings = settings.network) { settings.contestUrl.value } companion object { private val outcomeMap = mapOf( diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/pcms/PCMSDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/pcms/PCMSDataSource.kt index 1a5cc87c1..82bcffc97 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/pcms/PCMSDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/pcms/PCMSDataSource.kt @@ -13,13 +13,13 @@ import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds -internal class PCMSDataSource(val settings: PCMSSettings, creds: Map) : FullReloadContestDataSource(5.seconds) { - private val login = settings.login?.get(creds) - private val password = settings.password?.get(creds) +internal class PCMSDataSource(val settings: PCMSSettings) : FullReloadContestDataSource(5.seconds) { + private val login = settings.login?.value + private val password = settings.password?.value private val dataLoader = xmlLoader( networkSettings = settings.network, login?.let { ClientAuth.Basic(login, password!!) }) { - settings.url + settings.url.value } val resultType = settings.resultType @@ -29,7 +29,7 @@ internal class PCMSDataSource(val settings: PCMSSettings, creds: Map { + private val delegate = serializer() + override val descriptor = delegate.descriptor + override fun deserialize(decoder: Decoder) = UrlOrLocalPath(delegate.deserialize(decoder)) + override fun serialize(encoder: Encoder, value: UrlOrLocalPath) = delegate.serialize(encoder, value.value) + } + + public override fun toString(): String = value +} + // I'd like to have them in cds files, but then serializing would be much harder -@JvmInline -@Serializable -public value class Credential(private val raw: String) { - public fun get(creds: Map) : String { - val prefix = "\$creds." - return if (raw.startsWith(prefix)) { - val name = raw.substring(prefix.length) - creds[name] ?: throw IllegalStateException("Credential $name not found") - } else { - raw +@Serializable(with = Credential.Serializer::class) +public class Credential(public val displayValue: String, public val value: String) { + internal class Serializer : KSerializer { + private val delegate = serializer() + override val descriptor = delegate.descriptor + override fun deserialize(decoder: Decoder) : Credential { + val raw = delegate.deserialize(decoder) + return Credential(raw, raw) } + override fun serialize(encoder: Encoder, value: Credential) = delegate.serialize(encoder, value.displayValue) } + + override fun toString(): String = displayValue } @Serializable @@ -67,14 +86,14 @@ public sealed class CDSSettings { return json.encodeToString(this) } - public fun toFlow(creds: Map) : Flow { - val raw = toDataSource(creds) + public fun toFlow(): Flow { + val raw = toDataSource() return when (val emulationSettings = emulation) { null -> raw.getFlow() else -> raw.getFlow().toEmulationFlow(emulationSettings.startTime, emulationSettings.speed) } } - internal abstract fun toDataSource(creds: Map): ContestDataSource + internal abstract fun toDataSource(): ContestDataSource internal companion object { private val json = Json { prettyPrint = true } @@ -87,19 +106,19 @@ public class NoopSettings( override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = NoopDataSource() + override fun toDataSource() = NoopDataSource() } @Serializable @SerialName("testsys") public class TestSysSettings( - public val url: String, + public val url: UrlOrLocalPath, @Serializable(with = TimeZoneSerializer::class) public val timeZone: TimeZone = TimeZone.of("Europe/Moscow"), override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = TestSysDataSource(this) + override fun toDataSource() = TestSysDataSource(this) } @Serializable @@ -107,7 +126,7 @@ public class TestSysSettings( public class CatsSettings( public val login: Credential, public val password: Credential, - public val url: String, + public val url: UrlOrLocalPath, @Serializable(with = TimeZoneSerializer::class) public val timeZone: TimeZone = TimeZone.of("Asia/Vladivostok"), public val resultType: ContestResultType = ContestResultType.ICPC, @@ -115,32 +134,32 @@ public class CatsSettings( override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = CATSDataSource(this, creds) + override fun toDataSource() = CATSDataSource(this) } @Serializable @SerialName("krsu") public class KRSUSettings( - public val submissionsUrl: String, - public val contestUrl: String, + public val submissionsUrl: UrlOrLocalPath, + public val contestUrl: UrlOrLocalPath, @Serializable(with = TimeZoneSerializer::class) public val timeZone: TimeZone = TimeZone.of("Asia/Bishkek"), override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = KRSUDataSource(this) + override fun toDataSource() = KRSUDataSource(this) } @Serializable @SerialName("ejudge") public class EjudgeSettings( - public val url: String, + public val url: UrlOrLocalPath, public val resultType: ContestResultType = ContestResultType.ICPC, public val timeZone: TimeZone = TimeZone.of("Europe/Moscow"), override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = EjudgeDataSource(this) + override fun toDataSource() = EjudgeDataSource(this) } @@ -154,7 +173,7 @@ public class YandexSettings( override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = YandexDataSource(this, creds) + override fun toDataSource() = YandexDataSource(this) } @Serializable @@ -167,43 +186,39 @@ public class CFSettings( override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null, ) : CDSSettings() { - override fun toDataSource(creds: Map) = CFDataSource(this, creds) + override fun toDataSource() = CFDataSource(this) } @Serializable @SerialName("pcms") public class PCMSSettings( - public val url: String, + public val url: UrlOrLocalPath, public val login: Credential? = null, public val password: Credential? = null, - public val problemsUrl: String? = null, + public val problemsUrl: UrlOrLocalPath? = null, public val resultType: ContestResultType = ContestResultType.ICPC, override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null, ) : CDSSettings() { - override fun toDataSource(creds: Map) = PCMSDataSource(this, creds) + override fun toDataSource() = PCMSDataSource(this) } @Serializable -public class ClicsLoaderSettings( - public val url: String, +public class ClicsFeed( + public val url: UrlOrLocalPath, + public val contestId: String, public val login: Credential? = null, public val password: Credential? = null, public val eventFeedName: String = "event-feed", + public val eventFeedPath: String? = null, public val feedVersion: ClicsSettings.FeedVersion = ClicsSettings.FeedVersion.`2022_07` ) @SerialName("clics") @Serializable public class ClicsSettings( - private val url: String, - private val login: Credential? = null, - private val password: Credential? = null, - private val eventFeedName: String = "event-feed", - private val feedVersion: FeedVersion = FeedVersion.`2022_07`, - public val additionalFeed: ClicsLoaderSettings? = null, + public val feeds: List, public val useTeamNames: Boolean = true, - public val mediaBaseUrl: String = "", override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null, ) : CDSSettings() { @@ -212,9 +227,7 @@ public class ClicsSettings( `2022_07` } - public val mainFeed: ClicsLoaderSettings get() = ClicsLoaderSettings(url,login, password, eventFeedName, feedVersion) - - override fun toDataSource(creds: Map) = ClicsDataSource(this, creds) + override fun toDataSource() = ClicsDataSource(this) } @SerialName("codedrills") @@ -227,7 +240,7 @@ public class CodeDrillsSettings( override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null, ) : CDSSettings() { - override fun toDataSource(creds: Map) = CodeDrillsDataSource(this, creds) + override fun toDataSource() = CodeDrillsDataSource(this) } @SerialName("atcoder") @@ -242,7 +255,7 @@ public class AtcoderSettings( override val emulation: EmulationSettings? = null, override val network: NetworkSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = AtcoderDataSource(this, creds) + override fun toDataSource() = AtcoderDataSource(this) } @SerialName("cms") @@ -254,13 +267,41 @@ public class CmsSettings( override val network: NetworkSettings? = null, override val emulation: EmulationSettings? = null ) : CDSSettings() { - override fun toDataSource(creds: Map) = CmsDataSource(this) + override fun toDataSource() = CmsDataSource(this) } +public fun interface CredsProvider { + public operator fun get(s: String) : String? +} +public fun parseFileToCdsSettings(path: Path, creds: Map): CDSSettings = parseFileToCdsSettings(path) { creds[it] } @OptIn(ExperimentalSerializationApi::class) -public fun parseFileToCdsSettings(path: Path) : CDSSettings { +public fun parseFileToCdsSettings(path: Path, creds: CredsProvider) : CDSSettings { val file = path.toFile() + val module = SerializersModule { + postProcess( + onEncode = { + val prefix = "\$creds." + if (it.displayValue.startsWith(prefix)) { + val name = it.displayValue.substring(prefix.length) + Credential(it.displayValue, creds[name] ?: throw IllegalStateException("Credential $name not found")) + } else { + it + } + } + ) + postProcess( + onEncode = { + if (isHttpUrl(it.value)) { + it + } else { + val fixedPath = path.parent.resolve(it.value).toAbsolutePath() + require(fixedPath.exists()) { "File ${fixedPath} mentioned in settings doesn't exist"} + UrlOrLocalPath(fixedPath.toString()) + } + } + ) + } return if (!file.exists()) { throw java.lang.IllegalArgumentException("File ${file.absolutePath} does not exist") } else if (file.name.endsWith(".properties")) { @@ -304,11 +345,15 @@ public fun parseFileToCdsSettings(path: Path) : CDSSettings { Properties.decodeFromStringMap(properties as Map) } else if (file.name.endsWith(".json")) { file.inputStream().use { - Json.decodeFromStreamIgnoringComments(it) + Json { + serializersModule = module + }.decodeFromStreamIgnoringComments(it) } } else if (file.name.endsWith(".json5")) { file.inputStream().use { - Json5.decodeFromString(String(it.readAllBytes())) + Json5 { + serializersModule = module + }.decodeFromString(String(it.readAllBytes())) } } else { throw IllegalArgumentException("Unknown settings file extension: ${file.path}") diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/testsys/TestSysDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/testsys/TestSysDataSource.kt index 3c46f6649..d518e3582 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/testsys/TestSysDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/testsys/TestSysDataSource.kt @@ -12,7 +12,7 @@ import kotlin.time.Duration.Companion.seconds internal class TestSysDataSource(val settings: TestSysSettings) : FullReloadContestDataSource(5.seconds) { - val loader = ByteArrayLoader(settings.network, null) { settings.url } + val loader = ByteArrayLoader(settings.network, null) { settings.url.value } .map { val eofPosition = it.indexOf(EOF) String( diff --git a/src/cds/src/main/kotlin/org/icpclive/cds/yandex/YandexDataSource.kt b/src/cds/src/main/kotlin/org/icpclive/cds/yandex/YandexDataSource.kt index da2bc93ad..427ecc124 100644 --- a/src/cds/src/main/kotlin/org/icpclive/cds/yandex/YandexDataSource.kt +++ b/src/cds/src/main/kotlin/org/icpclive/cds/yandex/YandexDataSource.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.flow.* import kotlinx.serialization.json.Json import org.icpclive.api.ContestStatus import org.icpclive.cds.* -import org.icpclive.cds.adapters.withGroupedRuns import org.icpclive.cds.common.* import org.icpclive.cds.settings.YandexSettings import org.icpclive.cds.yandex.api.* @@ -17,8 +16,8 @@ import org.icpclive.util.* import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds -internal class YandexDataSource(settings: YandexSettings, creds: Map) : ContestDataSource { - private val apiKey = settings.apiKey.get(creds) +internal class YandexDataSource(settings: YandexSettings) : ContestDataSource { + private val apiKey = settings.apiKey.value private val httpClient: HttpClient private val contestDescriptionLoader: DataLoader diff --git a/src/cds/src/test/kotlin/org/icpclive/cds/AllConfigsAreParsable.kt b/src/cds/src/test/kotlin/org/icpclive/cds/AllConfigsAreParsable.kt index 3c5aad09d..691d4c322 100644 --- a/src/cds/src/test/kotlin/org/icpclive/cds/AllConfigsAreParsable.kt +++ b/src/cds/src/test/kotlin/org/icpclive/cds/AllConfigsAreParsable.kt @@ -14,10 +14,10 @@ class AllConfigsAreParsable { fun testSettings() : List { val configDir = Path("").absolute().parent.parent.resolve("config") return configDir.walk().filter { - it.name == "settings.json" || it.name == "settings.json5" + it.name == "settings.json" || it.name == "settings.json5" || it.name == "events.properties" && !it.pathString.contains("v2-configs") }.map { DynamicTest.dynamicTest(it.relativeTo(configDir).toString()) { - parseFileToCdsSettings(it) + parseFileToCdsSettings(it) { "" } } }.toList() } diff --git a/src/cds/src/test/kotlin/org/icpclive/cds/CdsLoadersTest.kt b/src/cds/src/test/kotlin/org/icpclive/cds/CdsLoadersTest.kt index b32a87966..fa22b3c13 100644 --- a/src/cds/src/test/kotlin/org/icpclive/cds/CdsLoadersTest.kt +++ b/src/cds/src/test/kotlin/org/icpclive/cds/CdsLoadersTest.kt @@ -25,7 +25,7 @@ object CdsLoadersTest { loaderTest( goldenDataDir.resolve("pcms.txt"), PCMSSettings( - url = "testData/loaders/pcms.xml" + url = UrlOrLocalPath("testData/loaders/pcms.xml") ) ) } @@ -36,7 +36,7 @@ object CdsLoadersTest { goldenDataDir.resolve("pcmsIOI.txt"), PCMSSettings( resultType = ContestResultType.IOI, - url = "testData/loaders/pcms-ioi.xml" + url = UrlOrLocalPath("testData/loaders/pcms-ioi.xml") ) ) } @@ -46,7 +46,7 @@ object CdsLoadersTest { loaderTest( goldenDataDir.resolve("ejudge.txt"), EjudgeSettings( - url = "testData/loaders/ejudge.xml" + url = UrlOrLocalPath("testData/loaders/ejudge.xml") ) ) } @@ -57,8 +57,14 @@ object CdsLoadersTest { loaderTest( goldenDataDir.resolve("clics202003.txt"), ClicsSettings( - url = "testData/loaders/clics-2020-03", - feedVersion = ClicsSettings.FeedVersion.`2020_03` + feeds = listOf( + ClicsFeed( + url = UrlOrLocalPath("testData/loaders/clics-2020-03"), + contestId = "", + eventFeedPath = "", + feedVersion = ClicsSettings.FeedVersion.`2020_03` + ) + ) ) ) } @@ -68,8 +74,14 @@ object CdsLoadersTest { loaderTest( goldenDataDir.resolve("clics202207.txt"), ClicsSettings( - url = "testData/loaders/clics-2022-07", - feedVersion = ClicsSettings.FeedVersion.`2022_07` + feeds = listOf( + ClicsFeed( + url = UrlOrLocalPath("testData/loaders/clics-2022-07"), + contestId = "", + eventFeedPath = "", + feedVersion = ClicsSettings.FeedVersion.`2022_07` + ) + ) ) ) } @@ -79,7 +91,7 @@ object CdsLoadersTest { loaderTest( goldenDataDir.resolve("testSys.txt"), TestSysSettings( - url = "testData/loaders/testsys.dat" + url = UrlOrLocalPath("testData/loaders/testsys.dat") ) ) } @@ -89,7 +101,7 @@ object CdsLoadersTest { loaderTest( goldenDataDir.resolve("testSysWithAdvancedOverride.txt"), TestSysSettings( - url = "testData/loaders/testsys.dat" + url = UrlOrLocalPath("testData/loaders/testsys.dat") ), AdvancedProperties( teamRegexes = TeamRegexOverrides( @@ -119,7 +131,7 @@ object CdsLoadersTest { } private fun loaderTest(expectedFile: Path, args: CDSSettings, advanced: AdvancedProperties? = null) { - val loader = args.toFlow(emptyMap()) + val loader = args.toFlow() val result = runBlocking { val result = withTimeout(1.minutes) { loader.finalContestState().let { diff --git a/src/cds/testData/loaders/goldenData/clics202003.txt b/src/cds/testData/loaders/goldenData/clics202003.txt index a55dcd8ae..cd5010923 100644 --- a/src/cds/testData/loaders/goldenData/clics202003.txt +++ b/src/cds/testData/loaders/goldenData/clics202003.txt @@ -141,7 +141,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/1/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/1/photo.jpg" } }, "isHidden": false, @@ -163,7 +163,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/10/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/10/photo.jpg" } }, "isHidden": false, @@ -203,7 +203,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/101/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/101/photo.jpg" } }, "isHidden": false, @@ -225,7 +225,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/102/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/102/photo.jpg" } }, "isHidden": false, @@ -247,7 +247,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/103/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/103/photo.jpg" } }, "isHidden": false, @@ -269,7 +269,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/104/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/104/photo.jpg" } }, "isHidden": false, @@ -291,7 +291,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/105/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/105/photo.jpg" } }, "isHidden": false, @@ -313,7 +313,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/106/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/106/photo.jpg" } }, "isHidden": false, @@ -335,7 +335,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/107/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/107/photo.jpg" } }, "isHidden": false, @@ -357,7 +357,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/108/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/108/photo.jpg" } }, "isHidden": false, @@ -379,7 +379,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/109/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/109/photo.jpg" } }, "isHidden": false, @@ -401,7 +401,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/11/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/11/photo.jpg" } }, "isHidden": false, @@ -441,7 +441,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/111/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/111/photo.jpg" } }, "isHidden": false, @@ -463,7 +463,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/112/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/112/photo.jpg" } }, "isHidden": false, @@ -485,7 +485,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/113/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/113/photo.jpg" } }, "isHidden": false, @@ -507,7 +507,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/114/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/114/photo.jpg" } }, "isHidden": false, @@ -529,7 +529,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/115/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/115/photo.jpg" } }, "isHidden": false, @@ -551,7 +551,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/116/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/116/photo.jpg" } }, "isHidden": false, @@ -573,7 +573,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/117/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/117/photo.jpg" } }, "isHidden": false, @@ -595,7 +595,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/118/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/118/photo.jpg" } }, "isHidden": false, @@ -617,7 +617,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/119/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/119/photo.jpg" } }, "isHidden": false, @@ -639,7 +639,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/12/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/12/photo.jpg" } }, "isHidden": false, @@ -661,7 +661,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/13/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/13/photo.jpg" } }, "isHidden": false, @@ -683,7 +683,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/14/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/14/photo.jpg" } }, "isHidden": false, @@ -705,7 +705,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/16/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/16/photo.jpg" } }, "isHidden": false, @@ -727,7 +727,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/17/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/17/photo.jpg" } }, "isHidden": false, @@ -749,7 +749,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/18/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/18/photo.jpg" } }, "isHidden": false, @@ -771,7 +771,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/19/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/19/photo.jpg" } }, "isHidden": false, @@ -793,7 +793,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/2/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/2/photo.jpg" } }, "isHidden": false, @@ -815,7 +815,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/20/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/20/photo.jpg" } }, "isHidden": false, @@ -837,7 +837,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/21/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/21/photo.jpg" } }, "isHidden": false, @@ -859,7 +859,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/22/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/22/photo.jpg" } }, "isHidden": false, @@ -881,7 +881,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/23/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/23/photo.jpg" } }, "isHidden": false, @@ -903,7 +903,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/24/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/24/photo.jpg" } }, "isHidden": false, @@ -925,7 +925,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/25/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/25/photo.jpg" } }, "isHidden": false, @@ -947,7 +947,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/26/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/26/photo.jpg" } }, "isHidden": false, @@ -987,7 +987,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/28/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/28/photo.jpg" } }, "isHidden": false, @@ -1009,7 +1009,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/29/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/29/photo.jpg" } }, "isHidden": false, @@ -1031,7 +1031,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/3/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/3/photo.jpg" } }, "isHidden": false, @@ -1053,7 +1053,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/30/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/30/photo.jpg" } }, "isHidden": false, @@ -1075,7 +1075,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/31/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/31/photo.jpg" } }, "isHidden": false, @@ -1097,7 +1097,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/32/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/32/photo.jpg" } }, "isHidden": false, @@ -1119,7 +1119,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/33/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/33/photo.jpg" } }, "isHidden": false, @@ -1141,7 +1141,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/34/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/34/photo.jpg" } }, "isHidden": false, @@ -1163,7 +1163,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/35/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/35/photo.jpg" } }, "isHidden": false, @@ -1185,7 +1185,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/36/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/36/photo.jpg" } }, "isHidden": false, @@ -1207,7 +1207,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/37/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/37/photo.jpg" } }, "isHidden": false, @@ -1229,7 +1229,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/38/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/38/photo.jpg" } }, "isHidden": false, @@ -1251,7 +1251,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/39/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/39/photo.jpg" } }, "isHidden": false, @@ -1273,7 +1273,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/4/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/4/photo.jpg" } }, "isHidden": false, @@ -1295,7 +1295,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/40/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/40/photo.jpg" } }, "isHidden": false, @@ -1317,7 +1317,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/41/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/41/photo.jpg" } }, "isHidden": false, @@ -1339,7 +1339,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/42/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/42/photo.jpg" } }, "isHidden": false, @@ -1361,7 +1361,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/43/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/43/photo.jpg" } }, "isHidden": false, @@ -1383,7 +1383,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/44/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/44/photo.jpg" } }, "isHidden": false, @@ -1405,7 +1405,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/45/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/45/photo.jpg" } }, "isHidden": false, @@ -1427,7 +1427,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/46/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/46/photo.jpg" } }, "isHidden": false, @@ -1449,7 +1449,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/47/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/47/photo.jpg" } }, "isHidden": false, @@ -1471,7 +1471,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/48/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/48/photo.jpg" } }, "isHidden": false, @@ -1493,7 +1493,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/49/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/49/photo.jpg" } }, "isHidden": false, @@ -1515,7 +1515,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/5/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/5/photo.jpg" } }, "isHidden": false, @@ -1537,7 +1537,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/50/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/50/photo.jpg" } }, "isHidden": false, @@ -1559,7 +1559,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/51/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/51/photo.jpg" } }, "isHidden": false, @@ -1581,7 +1581,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/52/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/52/photo.jpg" } }, "isHidden": false, @@ -1603,7 +1603,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/53/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/53/photo.jpg" } }, "isHidden": false, @@ -1625,7 +1625,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/54/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/54/photo.jpg" } }, "isHidden": false, @@ -1647,7 +1647,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/55/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/55/photo.jpg" } }, "isHidden": false, @@ -1669,7 +1669,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/56/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/56/photo.jpg" } }, "isHidden": false, @@ -1691,7 +1691,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/58/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/58/photo.jpg" } }, "isHidden": false, @@ -1713,7 +1713,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/59/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/59/photo.jpg" } }, "isHidden": false, @@ -1735,7 +1735,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/6/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/6/photo.jpg" } }, "isHidden": false, @@ -1775,7 +1775,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/61/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/61/photo.jpg" } }, "isHidden": false, @@ -1797,7 +1797,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/62/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/62/photo.jpg" } }, "isHidden": false, @@ -1819,7 +1819,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/63/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/63/photo.jpg" } }, "isHidden": false, @@ -1841,7 +1841,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/64/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/64/photo.jpg" } }, "isHidden": false, @@ -1863,7 +1863,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/65/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/65/photo.jpg" } }, "isHidden": false, @@ -1885,7 +1885,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/66/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/66/photo.jpg" } }, "isHidden": false, @@ -1907,7 +1907,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/67/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/67/photo.jpg" } }, "isHidden": false, @@ -1929,7 +1929,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/68/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/68/photo.jpg" } }, "isHidden": false, @@ -1951,7 +1951,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/69/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/69/photo.jpg" } }, "isHidden": false, @@ -1973,7 +1973,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/7/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/7/photo.jpg" } }, "isHidden": false, @@ -1995,7 +1995,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/70/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/70/photo.jpg" } }, "isHidden": false, @@ -2017,7 +2017,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/71/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/71/photo.jpg" } }, "isHidden": false, @@ -2039,7 +2039,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/72/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/72/photo.jpg" } }, "isHidden": false, @@ -2061,7 +2061,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/73/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/73/photo.jpg" } }, "isHidden": false, @@ -2083,7 +2083,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/74/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/74/photo.jpg" } }, "isHidden": false, @@ -2105,7 +2105,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/75/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/75/photo.jpg" } }, "isHidden": false, @@ -2127,7 +2127,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/76/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/76/photo.jpg" } }, "isHidden": false, @@ -2149,7 +2149,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/77/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/77/photo.jpg" } }, "isHidden": false, @@ -2171,7 +2171,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/78/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/78/photo.jpg" } }, "isHidden": false, @@ -2193,7 +2193,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/79/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/79/photo.jpg" } }, "isHidden": false, @@ -2215,7 +2215,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/8/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/8/photo.jpg" } }, "isHidden": false, @@ -2273,7 +2273,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/82/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/82/photo.jpg" } }, "isHidden": false, @@ -2295,7 +2295,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/83/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/83/photo.jpg" } }, "isHidden": false, @@ -2317,7 +2317,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/84/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/84/photo.jpg" } }, "isHidden": false, @@ -2339,7 +2339,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/85/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/85/photo.jpg" } }, "isHidden": false, @@ -2361,7 +2361,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/86/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/86/photo.jpg" } }, "isHidden": false, @@ -2383,7 +2383,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/87/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/87/photo.jpg" } }, "isHidden": false, @@ -2405,7 +2405,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/88/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/88/photo.jpg" } }, "isHidden": false, @@ -2427,7 +2427,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/89/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/89/photo.jpg" } }, "isHidden": false, @@ -2485,7 +2485,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/91/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/91/photo.jpg" } }, "isHidden": false, @@ -2507,7 +2507,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/92/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/92/photo.jpg" } }, "isHidden": false, @@ -2529,7 +2529,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/93/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/93/photo.jpg" } }, "isHidden": false, @@ -2551,7 +2551,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/94/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/94/photo.jpg" } }, "isHidden": false, @@ -2573,7 +2573,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/95/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/95/photo.jpg" } }, "isHidden": false, @@ -2595,7 +2595,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/96/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/96/photo.jpg" } }, "isHidden": false, @@ -2617,7 +2617,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/97/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/97/photo.jpg" } }, "isHidden": false, @@ -2639,7 +2639,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/98/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/98/photo.jpg" } }, "isHidden": false, @@ -2661,7 +2661,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/finals/teams/99/photo.jpg" + "url": "testData/loaders/clics-2020-03/contests/finals/teams/99/photo.jpg" } }, "isHidden": false, diff --git a/src/cds/testData/loaders/goldenData/clics202207.txt b/src/cds/testData/loaders/goldenData/clics202207.txt index 00eca95fc..e148079cd 100644 --- a/src/cds/testData/loaders/goldenData/clics202207.txt +++ b/src/cds/testData/loaders/goldenData/clics202207.txt @@ -117,7 +117,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/1/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/1/photo" } }, "isHidden": false, @@ -139,7 +139,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/2/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/2/photo" } }, "isHidden": false, @@ -161,7 +161,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/3/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/3/photo" } }, "isHidden": false, @@ -183,7 +183,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/4/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/4/photo" } }, "isHidden": false, @@ -205,7 +205,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/5/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/5/photo" } }, "isHidden": false, @@ -227,7 +227,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/6/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/6/photo" } }, "isHidden": false, @@ -249,7 +249,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/7/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/7/photo" } }, "isHidden": false, @@ -271,7 +271,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/8/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/8/photo" } }, "isHidden": false, @@ -293,7 +293,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/9/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/9/photo" } }, "isHidden": false, @@ -315,7 +315,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/10/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/10/photo" } }, "isHidden": false, @@ -337,7 +337,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/11/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/11/photo" } }, "isHidden": false, @@ -359,7 +359,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/13/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/13/photo" } }, "isHidden": false, @@ -381,7 +381,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/14/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/14/photo" } }, "isHidden": false, @@ -403,7 +403,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/15/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/15/photo" } }, "isHidden": false, @@ -425,7 +425,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/16/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/16/photo" } }, "isHidden": false, @@ -447,7 +447,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/17/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/17/photo" } }, "isHidden": false, @@ -469,7 +469,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/18/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/18/photo" } }, "isHidden": false, @@ -491,7 +491,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/19/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/19/photo" } }, "isHidden": false, @@ -513,7 +513,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/20/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/20/photo" } }, "isHidden": false, @@ -535,7 +535,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/21/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/21/photo" } }, "isHidden": false, @@ -557,7 +557,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/22/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/22/photo" } }, "isHidden": false, @@ -579,7 +579,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/23/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/23/photo" } }, "isHidden": false, @@ -601,7 +601,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/24/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/24/photo" } }, "isHidden": false, @@ -623,7 +623,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/25/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/25/photo" } }, "isHidden": false, @@ -645,7 +645,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/26/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/26/photo" } }, "isHidden": false, @@ -667,7 +667,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/27/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/27/photo" } }, "isHidden": false, @@ -689,7 +689,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/28/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/28/photo" } }, "isHidden": false, @@ -711,7 +711,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/29/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/29/photo" } }, "isHidden": false, @@ -733,7 +733,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/30/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/30/photo" } }, "isHidden": false, @@ -755,7 +755,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/31/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/31/photo" } }, "isHidden": false, @@ -777,7 +777,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/32/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/32/photo" } }, "isHidden": false, @@ -799,7 +799,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/33/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/33/photo" } }, "isHidden": false, @@ -821,7 +821,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/34/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/34/photo" } }, "isHidden": false, @@ -843,7 +843,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/35/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/35/photo" } }, "isHidden": false, @@ -865,7 +865,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/36/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/36/photo" } }, "isHidden": false, @@ -887,7 +887,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/37/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/37/photo" } }, "isHidden": false, @@ -909,7 +909,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/38/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/38/photo" } }, "isHidden": false, @@ -931,7 +931,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/39/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/39/photo" } }, "isHidden": false, @@ -953,7 +953,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/40/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/40/photo" } }, "isHidden": false, @@ -975,7 +975,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/41/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/41/photo" } }, "isHidden": false, @@ -997,7 +997,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/42/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/42/photo" } }, "isHidden": false, @@ -1019,7 +1019,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/43/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/43/photo" } }, "isHidden": false, @@ -1041,7 +1041,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/44/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/44/photo" } }, "isHidden": false, @@ -1063,7 +1063,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/45/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/45/photo" } }, "isHidden": false, @@ -1085,7 +1085,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/46/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/46/photo" } }, "isHidden": false, @@ -1107,7 +1107,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/47/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/47/photo" } }, "isHidden": false, @@ -1129,7 +1129,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/48/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/48/photo" } }, "isHidden": false, @@ -1151,7 +1151,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/49/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/49/photo" } }, "isHidden": false, @@ -1173,7 +1173,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/50/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/50/photo" } }, "isHidden": false, @@ -1195,7 +1195,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/51/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/51/photo" } }, "isHidden": false, @@ -1217,7 +1217,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/52/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/52/photo" } }, "isHidden": false, @@ -1239,7 +1239,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/53/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/53/photo" } }, "isHidden": false, @@ -1261,7 +1261,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/54/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/54/photo" } }, "isHidden": false, @@ -1283,7 +1283,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/55/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/55/photo" } }, "isHidden": false, @@ -1305,7 +1305,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/56/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/56/photo" } }, "isHidden": false, @@ -1327,7 +1327,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/57/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/57/photo" } }, "isHidden": false, @@ -1349,7 +1349,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/58/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/58/photo" } }, "isHidden": false, @@ -1371,7 +1371,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/59/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/59/photo" } }, "isHidden": false, @@ -1393,7 +1393,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/60/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/60/photo" } }, "isHidden": false, @@ -1415,7 +1415,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/61/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/61/photo" } }, "isHidden": false, @@ -1437,7 +1437,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/62/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/62/photo" } }, "isHidden": false, @@ -1459,7 +1459,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/63/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/63/photo" } }, "isHidden": false, @@ -1481,7 +1481,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/64/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/64/photo" } }, "isHidden": false, @@ -1503,7 +1503,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/65/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/65/photo" } }, "isHidden": false, @@ -1525,7 +1525,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/66/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/66/photo" } }, "isHidden": false, @@ -1547,7 +1547,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/67/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/67/photo" } }, "isHidden": false, @@ -1569,7 +1569,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/68/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/68/photo" } }, "isHidden": false, @@ -1591,7 +1591,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/69/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/69/photo" } }, "isHidden": false, @@ -1613,7 +1613,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/70/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/70/photo" } }, "isHidden": false, @@ -1635,7 +1635,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/71/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/71/photo" } }, "isHidden": false, @@ -1657,7 +1657,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/72/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/72/photo" } }, "isHidden": false, @@ -1679,7 +1679,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/73/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/73/photo" } }, "isHidden": false, @@ -1701,7 +1701,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/74/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/74/photo" } }, "isHidden": false, @@ -1723,7 +1723,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/75/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/75/photo" } }, "isHidden": false, @@ -1745,7 +1745,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/76/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/76/photo" } }, "isHidden": false, @@ -1767,7 +1767,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/77/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/77/photo" } }, "isHidden": false, @@ -1789,7 +1789,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/78/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/78/photo" } }, "isHidden": false, @@ -1811,7 +1811,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/79/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/79/photo" } }, "isHidden": false, @@ -1833,7 +1833,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/80/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/80/photo" } }, "isHidden": false, @@ -1855,7 +1855,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/81/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/81/photo" } }, "isHidden": false, @@ -1877,7 +1877,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/82/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/82/photo" } }, "isHidden": false, @@ -1899,7 +1899,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/83/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/83/photo" } }, "isHidden": false, @@ -1921,7 +1921,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/84/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/84/photo" } }, "isHidden": false, @@ -1943,7 +1943,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/85/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/85/photo" } }, "isHidden": false, @@ -1965,7 +1965,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/86/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/86/photo" } }, "isHidden": false, @@ -1987,7 +1987,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/87/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/87/photo" } }, "isHidden": false, @@ -2009,7 +2009,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/88/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/88/photo" } }, "isHidden": false, @@ -2031,7 +2031,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/89/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/89/photo" } }, "isHidden": false, @@ -2053,7 +2053,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/90/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/90/photo" } }, "isHidden": false, @@ -2075,7 +2075,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/91/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/91/photo" } }, "isHidden": false, @@ -2097,7 +2097,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/92/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/92/photo" } }, "isHidden": false, @@ -2119,7 +2119,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/93/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/93/photo" } }, "isHidden": false, @@ -2141,7 +2141,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/94/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/94/photo" } }, "isHidden": false, @@ -2163,7 +2163,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/95/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/95/photo" } }, "isHidden": false, @@ -2185,7 +2185,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/96/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/96/photo" } }, "isHidden": false, @@ -2207,7 +2207,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/97/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/97/photo" } }, "isHidden": false, @@ -2229,7 +2229,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/98/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/98/photo" } }, "isHidden": false, @@ -2251,7 +2251,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/99/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/99/photo" } }, "isHidden": false, @@ -2273,7 +2273,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/100/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/100/photo" } }, "isHidden": false, @@ -2295,7 +2295,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/101/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/101/photo" } }, "isHidden": false, @@ -2317,7 +2317,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/102/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/102/photo" } }, "isHidden": false, @@ -2339,7 +2339,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/103/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/103/photo" } }, "isHidden": false, @@ -2361,7 +2361,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/104/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/104/photo" } }, "isHidden": false, @@ -2383,7 +2383,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/105/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/105/photo" } }, "isHidden": false, @@ -2405,7 +2405,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/106/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/106/photo" } }, "isHidden": false, @@ -2427,7 +2427,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/107/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/107/photo" } }, "isHidden": false, @@ -2449,7 +2449,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/108/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/108/photo" } }, "isHidden": false, @@ -2471,7 +2471,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/109/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/109/photo" } }, "isHidden": false, @@ -2493,7 +2493,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/110/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/110/photo" } }, "isHidden": false, @@ -2515,7 +2515,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/111/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/111/photo" } }, "isHidden": false, @@ -2537,7 +2537,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/112/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/112/photo" } }, "isHidden": false, @@ -2559,7 +2559,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/113/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/113/photo" } }, "isHidden": false, @@ -2581,7 +2581,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/114/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/114/photo" } }, "isHidden": false, @@ -2603,7 +2603,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/115/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/115/photo" } }, "isHidden": false, @@ -2625,7 +2625,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/116/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/116/photo" } }, "isHidden": false, @@ -2647,7 +2647,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/117/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/117/photo" } }, "isHidden": false, @@ -2669,7 +2669,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/118/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/118/photo" } }, "isHidden": false, @@ -2691,7 +2691,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/119/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/119/photo" } }, "isHidden": false, @@ -2713,7 +2713,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/120/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/120/photo" } }, "isHidden": false, @@ -2735,7 +2735,7 @@ "medias": { "photo": { "type": "Photo", - "url": "/contests/swerc2022/teams/121/photo" + "url": "testData/loaders/clics-2022-07/contests/swerc2022/teams/121/photo" } }, "isHidden": false, diff --git a/src/clics-api/src/main/kotlin/org/icpclive/clics/Objects.kt b/src/clics-api/src/main/kotlin/org/icpclive/clics/Objects.kt index e4d97c3df..5a50f2115 100644 --- a/src/clics-api/src/main/kotlin/org/icpclive/clics/Objects.kt +++ b/src/clics-api/src/main/kotlin/org/icpclive/clics/Objects.kt @@ -1,10 +1,11 @@ @file:Suppress("UNUSED") - +@file:UseContextualSerialization(Media::class) package org.icpclive.clics import kotlinx.datetime.Instant -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import kotlinx.serialization.* +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import org.icpclive.util.ColorSerializer import org.icpclive.util.DurationInMinutesSerializer import java.awt.Color @@ -121,6 +122,7 @@ data class Organization( val id: String, val name: String = "", val formal_name: String? = null, + val country_flag: List = emptyList(), val logo: List = emptyList(), val twitter_hashtag: String? = null ) diff --git a/src/common/src/main/kotlin/org/icpclive/util/SerializationUtils.kt b/src/common/src/main/kotlin/org/icpclive/util/SerializationUtils.kt index 853f2c342..92e408411 100644 --- a/src/common/src/main/kotlin/org/icpclive/util/SerializationUtils.kt +++ b/src/common/src/main/kotlin/org/icpclive/util/SerializationUtils.kt @@ -2,15 +2,14 @@ package org.icpclive.util import kotlinx.datetime.Instant import kotlinx.datetime.TimeZone -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationException +import kotlinx.serialization.* import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.* +import kotlinx.serialization.modules.SerializersModuleBuilder import java.awt.Color import java.io.InputStream import java.lang.Exception @@ -196,4 +195,16 @@ inline fun Json.decodeFromStringIgnoringComments(data: String) : T = is JsonObject -> JsonObject(filter { !it.key.startsWith("#") }.mapValues { it.value.cleanFromComments() }) is JsonPrimitive, JsonNull -> this } +} + +inline fun SerializersModuleBuilder.postProcess( + crossinline onEncode: (T) -> T = { it }, + crossinline onDecode: (T) -> T = { it }, +) { + contextual(T::class, object : KSerializer { + private val delegate = serializer() + override val descriptor get() = delegate.descriptor + override fun deserialize(decoder: Decoder) = onEncode(delegate.deserialize(decoder)) + override fun serialize(encoder: Encoder, value: T) = delegate.serialize(encoder, onDecode(value)) + }) } \ No newline at end of file diff --git a/src/frontend/build.gradle.kts b/src/frontend/build.gradle.kts index d1ba5eb0d..3db058ddb 100644 --- a/src/frontend/build.gradle.kts +++ b/src/frontend/build.gradle.kts @@ -21,8 +21,8 @@ tasks { environment.set(mapOf("PUBLIC_URL" to "/overlay", "BUILD_PATH" to "build")) inputs.dir("overlay/src") inputs.file("overlay/index.html") - inputs.file("package.json") inputs.dir("common") + inputs.file("package.json") inputs.file("package-lock.json") inputs.file("overlay/package.json") outputs.dir("overlay/build") @@ -32,6 +32,7 @@ tasks { environment.set(mapOf("PUBLIC_URL" to "/admin")) inputs.dir("admin/src") inputs.dir("admin/public") + inputs.dir("common") inputs.file("package.json") inputs.file("package-lock.json") inputs.file("admin/package.json") diff --git a/src/frontend/common/api.ts b/src/frontend/common/api.ts new file mode 100644 index 000000000..f7687d5d4 --- /dev/null +++ b/src/frontend/common/api.ts @@ -0,0 +1,635 @@ +export interface ContestInfo { + name: string; + status: ContestStatus; + resultType: ContestResultType; + startTimeUnixMs: number; + contestLengthMs: number; + freezeTimeMs: number; + problems: ProblemInfo[]; + teams: TeamInfo[]; + groups: GroupInfo[]; + organizations: OrganizationInfo[]; + penaltyRoundingMode: PenaltyRoundingMode; + holdBeforeStartTimeMs?: number | null; + emulationSpeed?: number; + medals?: MedalType[]; + penaltyPerWrongAttempt?: string; +} + +export enum ContestStatus { + BEFORE = "BEFORE", + RUNNING = "RUNNING", + OVER = "OVER", + FINALIZED = "FINALIZED", +} + +export enum ContestResultType { + ICPC = "ICPC", + IOI = "IOI", +} + +export enum PenaltyRoundingMode { + each_submission_down_to_minute = "each_submission_down_to_minute", + sum_down_to_minute = "sum_down_to_minute", + sum_in_seconds = "sum_in_seconds", + last = "last", + zero = "zero", +} + +export interface ProblemInfo { + letter: string; + name: string; + id: number; + ordinal: number; + contestSystemId: string; + minScore?: number | null; + maxScore?: number | null; + color?: string | null; + scoreMergeMode?: ScoreMergeMode | null; +} + +export interface TeamInfo { + id: number; + name: string; + shortName: string; + contestSystemId: string; + groups: string[]; + hashTag: string | null; + medias: { [key in TeamMediaType]: MediaType }; + isHidden: boolean; + isOutOfContest: boolean; + organizationId: string | null; + customFields?: { [key: string]: string }; +} + +export interface GroupInfo { + name: string; + isHidden: boolean; + isOutOfContest: boolean; +} + +export interface OrganizationInfo { + cdsId: string; + displayName: string; + fullName: string; +} + +export interface MedalType { + name: string; + count: number; + minScore?: number; + tiebreakMode?: MedalTiebreakMode; +} + +export enum ScoreMergeMode { + MAX_PER_GROUP = "MAX_PER_GROUP", + MAX_TOTAL = "MAX_TOTAL", + LAST = "LAST", + LAST_OK = "LAST_OK", + SUM = "SUM", +} + +export enum MedalTiebreakMode { + NONE = "NONE", + ALL = "ALL", +} + +export enum TeamMediaType { + camera = "camera", + screen = "screen", + record = "record", + photo = "photo", + reactionVideo = "reactionVideo", + achievement = "achievement", +} + +export type MediaType = + | MediaType.Object + | MediaType.Photo + | MediaType.TaskStatus + | MediaType.Video + | MediaType.WebRTCGrabberConnection + | MediaType.WebRTCProxyConnection; + +export namespace MediaType { + export enum Type { + Object = "Object", + Photo = "Photo", + TaskStatus = "TaskStatus", + Video = "Video", + WebRTCGrabberConnection = "WebRTCGrabberConnection", + WebRTCProxyConnection = "WebRTCProxyConnection", + } + + export interface Object { + type: MediaType.Type.Object; + url: string; + isMedia?: boolean; + } + + export interface Photo { + type: MediaType.Type.Photo; + url: string; + isMedia?: boolean; + } + + export interface TaskStatus { + type: MediaType.Type.TaskStatus; + teamId: number; + isMedia?: boolean; + } + + export interface Video { + type: MediaType.Type.Video; + url: string; + isMedia?: boolean; + } + + export interface WebRTCGrabberConnection { + type: MediaType.Type.WebRTCGrabberConnection; + url: string; + peerName: string; + streamType: string; + credential: string | null; + isMedia?: boolean; + } + + export interface WebRTCProxyConnection { + type: MediaType.Type.WebRTCProxyConnection; + url: string; + audioUrl?: string | null; + isMedia?: boolean; + } +} + +export interface RunInfo { + id: number; + result: RunResult | null; + percentage: number; + problemId: number; + teamId: number; + time: number; + featuredRunMedia?: MediaType | null; + reactionVideos?: MediaType[]; + isHidden?: boolean; +} + +export type RunResult = + | RunResult.ICPC + | RunResult.IOI; + +export namespace RunResult { + export enum Type { + ICPC = "ICPC", + IOI = "IOI", + } + + export interface ICPC { + type: RunResult.Type.ICPC; + verdict: Verdict; + isFirstToSolveRun: boolean; + } + + export interface IOI { + type: RunResult.Type.IOI; + score: number[]; + wrongVerdict?: Verdict | null; + difference?: number; + scoreAfter?: number; + isFirstBestRun?: boolean; + isFirstBestTeamRun?: boolean; + } +} + +export interface Verdict { + shortName: string; + isAddingPenalty: boolean; + isAccepted: boolean; +} + +export interface Scoreboard { + lastSubmitTime: number; + rows: ScoreboardRow[]; +} + +export interface ScoreboardRow { + teamId: number; + rank: number; + totalScore: number; + penalty: number; + lastAccepted: number; + medalType: string | null; + problemResults: ProblemResult[]; + teamGroups: string[]; + championInGroups: string[]; +} + +export type ProblemResult = + | ProblemResult.ICPC + | ProblemResult.IOI; + +export namespace ProblemResult { + export enum Type { + ICPC = "ICPC", + IOI = "IOI", + } + + export interface ICPC { + type: ProblemResult.Type.ICPC; + wrongAttempts: number; + pendingAttempts: number; + isSolved: boolean; + isFirstToSolve: boolean; + lastSubmitTimeMs: number | null; + } + + export interface IOI { + type: ProblemResult.Type.IOI; + score: number | null; + lastSubmitTimeMs: number | null; + isFirstBest: boolean; + } +} + +export type MainScreenEvent = + | MainScreenEvent.HideWidget + | MainScreenEvent.MainScreenSnapshot + | MainScreenEvent.ShowWidget; + +export namespace MainScreenEvent { + export enum Type { + HideWidget = "HideWidget", + MainScreenSnapshot = "MainScreenSnapshot", + ShowWidget = "ShowWidget", + } + + export interface HideWidget { + type: MainScreenEvent.Type.HideWidget; + id: string; + } + + export interface MainScreenSnapshot { + type: MainScreenEvent.Type.MainScreenSnapshot; + widgets: Widget[]; + } + + export interface ShowWidget { + type: MainScreenEvent.Type.ShowWidget; + widget: Widget; + } +} + +export type Widget = + | Widget.AdvertisementWidget + | Widget.FullScreenClockWidget + | Widget.PictureWidget + | Widget.QueueWidget + | Widget.ScoreboardWidget + | Widget.StatisticsWidget + | Widget.SvgWidget + | Widget.TeamLocatorWidget + | Widget.TeamViewWidget + | Widget.TickerWidget; + +export namespace Widget { + export enum Type { + AdvertisementWidget = "AdvertisementWidget", + FullScreenClockWidget = "FullScreenClockWidget", + PictureWidget = "PictureWidget", + QueueWidget = "QueueWidget", + ScoreboardWidget = "ScoreboardWidget", + StatisticsWidget = "StatisticsWidget", + SvgWidget = "SvgWidget", + TeamLocatorWidget = "TeamLocatorWidget", + TeamViewWidget = "TeamViewWidget", + TickerWidget = "TickerWidget", + } + + export interface AdvertisementWidget { + type: Widget.Type.AdvertisementWidget; + widgetId: string; + location: LocationRectangle; + advertisement: AdvertisementSettings; + } + + export interface FullScreenClockWidget { + type: Widget.Type.FullScreenClockWidget; + widgetId: string; + location: LocationRectangle; + settings: FullScreenClockSettings; + } + + export interface PictureWidget { + type: Widget.Type.PictureWidget; + widgetId: string; + location: LocationRectangle; + picture: PictureSettings; + } + + export interface QueueWidget { + type: Widget.Type.QueueWidget; + widgetId: string; + location: LocationRectangle; + settings: QueueSettings; + } + + export interface ScoreboardWidget { + type: Widget.Type.ScoreboardWidget; + widgetId: string; + location: LocationRectangle; + settings: ScoreboardSettings; + } + + export interface StatisticsWidget { + type: Widget.Type.StatisticsWidget; + widgetId: string; + location: LocationRectangle; + settings: StatisticsSettings; + } + + export interface SvgWidget { + type: Widget.Type.SvgWidget; + widgetId: string; + location: LocationRectangle; + content: string; + } + + export interface TeamLocatorWidget { + type: Widget.Type.TeamLocatorWidget; + widgetId: string; + location: LocationRectangle; + settings: TeamLocatorSettings; + } + + export interface TeamViewWidget { + type: Widget.Type.TeamViewWidget; + widgetId: string; + location: LocationRectangle; + settings: OverlayTeamViewSettings; + } + + export interface TickerWidget { + type: Widget.Type.TickerWidget; + widgetId: string; + location: LocationRectangle; + settings: TickerSettings; + } +} + +export interface LocationRectangle { + positionX: number; + positionY: number; + sizeX: number; + sizeY: number; +} + +export interface AdvertisementSettings { + text: string; +} + +export interface FullScreenClockSettings { + globalTimeMode?: boolean; + quietMode?: boolean; + contestCountdownMode?: boolean; +} + +export interface PictureSettings { + url: string; + name: string; +} + +export interface QueueSettings { +} + +export interface ScoreboardSettings { + isInfinite?: boolean; + numRows?: number | null; + startFromRow?: number; + optimismLevel?: OptimismLevel; + teamsOnPage?: number; + group?: string; +} + +export interface StatisticsSettings { +} + +export interface TeamLocatorSettings { + circles?: TeamLocatorCircleSettings[]; + scene?: string; +} + +export interface OverlayTeamViewSettings { + content?: MediaType[]; + position?: TeamViewPosition; +} + +export interface TickerSettings { +} + +export enum OptimismLevel { + normal = "normal", + optimistic = "optimistic", + pessimistic = "pessimistic", +} + +export enum TeamViewPosition { + SINGLE_TOP_RIGHT = "SINGLE_TOP_RIGHT", + PVP_TOP = "PVP_TOP", + PVP_BOTTOM = "PVP_BOTTOM", + TOP_LEFT = "TOP_LEFT", + TOP_RIGHT = "TOP_RIGHT", + BOTTOM_LEFT = "BOTTOM_LEFT", + BOTTOM_RIGHT = "BOTTOM_RIGHT", +} + +export interface TeamLocatorCircleSettings { + x: number; + y: number; + radius: number; + teamId: number; +} + +export type QueueEvent = + | QueueEvent.AddRunToQueue + | QueueEvent.ModifyRunInQueue + | QueueEvent.QueueSnapshot + | QueueEvent.RemoveRunFromQueue; + +export namespace QueueEvent { + export enum Type { + AddRunToQueue = "AddRunToQueue", + ModifyRunInQueue = "ModifyRunInQueue", + QueueSnapshot = "QueueSnapshot", + RemoveRunFromQueue = "RemoveRunFromQueue", + } + + export interface AddRunToQueue { + type: QueueEvent.Type.AddRunToQueue; + info: RunInfo; + } + + export interface ModifyRunInQueue { + type: QueueEvent.Type.ModifyRunInQueue; + info: RunInfo; + } + + export interface QueueSnapshot { + type: QueueEvent.Type.QueueSnapshot; + infos: RunInfo[]; + } + + export interface RemoveRunFromQueue { + type: QueueEvent.Type.RemoveRunFromQueue; + info: RunInfo; + } +} + +export type AnalyticsEvent = + | AnalyticsEvent.AddAnalyticsMessage + | AnalyticsEvent.AnalyticsMessageSnapshot + | AnalyticsEvent.ModifyAnalyticsMessage; + +export namespace AnalyticsEvent { + export enum Type { + AddAnalyticsMessage = "AddAnalyticsMessage", + AnalyticsMessageSnapshot = "AnalyticsMessageSnapshot", + ModifyAnalyticsMessage = "ModifyAnalyticsMessage", + } + + export interface AddAnalyticsMessage { + type: AnalyticsEvent.Type.AddAnalyticsMessage; + message: AnalyticsMessage; + } + + export interface AnalyticsMessageSnapshot { + type: AnalyticsEvent.Type.AnalyticsMessageSnapshot; + messages: AnalyticsMessage[]; + } + + export interface ModifyAnalyticsMessage { + type: AnalyticsEvent.Type.ModifyAnalyticsMessage; + message: AnalyticsMessage; + } +} + +export type AnalyticsMessage = + | AnalyticsMessage.commentary; + +export namespace AnalyticsMessage { + export enum Type { + commentary = "commentary", + } + + export interface commentary { + type: AnalyticsMessage.Type.commentary; + id: string; + message: string; + timeUnixMs: number; + relativeTimeMs: number; + teamIds: number[]; + runIds: number[]; + priority?: number; + tags?: string[]; + advertisement?: AnalyticsCompanionPreset | null; + tickerMessage?: AnalyticsCompanionPreset | null; + featuredRun?: AnalyticsCompanionRun | null; + } +} + +export interface AnalyticsCompanionPreset { + presetId: number; + expirationTimeUnixMs: number | null; +} + +export interface AnalyticsCompanionRun { + expirationTimeUnixMs: number | null; + mediaType: MediaType; +} + +export type TickerEvent = + | TickerEvent.AddMessage + | TickerEvent.RemoveMessage + | TickerEvent.TickerSnapshot; + +export namespace TickerEvent { + export enum Type { + AddMessage = "AddMessage", + RemoveMessage = "RemoveMessage", + TickerSnapshot = "TickerSnapshot", + } + + export interface AddMessage { + type: TickerEvent.Type.AddMessage; + message: TickerMessage; + } + + export interface RemoveMessage { + type: TickerEvent.Type.RemoveMessage; + messageId: string; + } + + export interface TickerSnapshot { + type: TickerEvent.Type.TickerSnapshot; + messages: TickerMessage[]; + } +} + +export type TickerMessage = + | TickerMessage.clock + | TickerMessage.scoreboard + | TickerMessage.text; + +export namespace TickerMessage { + export enum Type { + clock = "clock", + scoreboard = "scoreboard", + text = "text", + } + + export interface clock { + type: TickerMessage.Type.clock; + id: string; + part: TickerPart; + periodMs: number; + settings: clock; + } + + export interface scoreboard { + type: TickerMessage.Type.scoreboard; + id: string; + part: TickerPart; + periodMs: number; + settings: scoreboard; + } + + export interface text { + type: TickerMessage.Type.text; + id: string; + part: TickerPart; + periodMs: number; + settings: text; + } +} + +export enum TickerPart { + short = "short", + long = "long", +} + +export interface clock { + part: TickerPart; + periodMs: number; +} + +export interface scoreboard { + part: TickerPart; + periodMs: number; + from: number; + to: number; +} + +export interface text { + part: TickerPart; + periodMs: number; + text: string; +} diff --git a/src/reactions-bot/src/main/kotlin/org/icpclive/reacbot/Bot.kt b/src/reactions-bot/src/main/kotlin/org/icpclive/reacbot/Bot.kt index 88d79ae81..9d28de3f2 100644 --- a/src/reactions-bot/src/main/kotlin/org/icpclive/reacbot/Bot.kt +++ b/src/reactions-bot/src/main/kotlin/org/icpclive/reacbot/Bot.kt @@ -33,7 +33,8 @@ class Bot(private val config: Config) { private val reactionsProcessingPool = newFixedThreadPoolContext(config.loaderThreads, "ReactionsProcessing") private val cds = parseFileToCdsSettings( Path.of(config.settingsFile), - ).toFlow(emptyMap()) + emptyMap() + ).toFlow() .contestState() .filterUseless() .removeFrozenSubmissions() diff --git a/src/schema-generator/build.gradle.kts b/src/schema-generator/build.gradle.kts index c5e190d8a..37098265c 100644 --- a/src/schema-generator/build.gradle.kts +++ b/src/schema-generator/build.gradle.kts @@ -3,36 +3,49 @@ plugins { alias(libs.plugins.kotlin.serialization) } -val schemasExportLocation = rootProject.rootDir.resolve("schemas/") -val tmpLocation = buildDir.resolve("tmp/") -val schemasGenerationLocation = tmpLocation.resolve("schemas/") -val schemasGatherLocation = buildDir.resolve("schemas/") +val tmpLocation = buildDir.resolve("tmp") +val schemasExportLocation = rootProject.rootDir.resolve("schemas") +val schemasGenerationLocation = tmpLocation.resolve("schemas") +val schemasGatherLocation = buildDir.resolve("schemas") + +val tsExportLocation = rootProject.rootDir.resolve("src").resolve("frontend").resolve("common") +val tsGenerationLocation = tmpLocation.resolve("ts") +val tsGatherLocation = buildDir.resolve("ts") + fun String.capitalize(): String = replaceFirstChar { it.uppercaseChar() } fun TaskContainerScope.genTask( - classPackage: String, - className: String, - fileName: String, - title: String + command: String, + classFqNames: List, + taskSuffix: String, + fullFileName: String, + title: String?, + exportLocation: File, + generationLocation: File, ): Pair, TaskProvider> { - val fullFileName = "$fileName.schema.json" - val generatedSchemaFile = schemasGenerationLocation.resolve(fullFileName) - val repositorySchemaFile = schemasExportLocation.resolve(fullFileName) + val generatedSchemaFile = generationLocation.resolve(fullFileName) + val repositorySchemaFile = exportLocation.resolve(fullFileName) - val genTask = register("generateSchema${className.capitalize()}") { + val genTask = register("generateSchema${taskSuffix}") { dependsOn(assemble) classpath = sourceSets.main.get().runtimeClasspath mainClass = "org.icpclive.generator.schema.GenKt" workingDir = tmpLocation outputs.file(generatedSchemaFile) - args = listOf( - "$classPackage.$className", - "--output", generatedSchemaFile.relativeTo(workingDir).path, - "--title", title - ) + args = buildList { + add(command) + classFqNames.forEach { + add("--class-name"); add(it) + } + add("--output"); add(generatedSchemaFile.relativeTo(workingDir).path) + if (title != null) { + add("--title"); add(title) + } + } + } - val checkTask = register("testSchema${className.capitalize()}") { + val checkTask = register("testSchema${taskSuffix}") { group = "verification" dependsOn(genTask) inputs.files(generatedSchemaFile, repositorySchemaFile) @@ -40,21 +53,56 @@ fun TaskContainerScope.genTask( val newContent = generatedSchemaFile.readText() val oldContent = repositorySchemaFile.readText() if (newContent != oldContent) { - throw IllegalStateException("Json schema for $className is outdated. Run `./gradlew :${project.name}:gen` to fix it.") + throw IllegalStateException("File $fullFileName is outdated. Run `./gradlew :${project.name}:gen` to fix it.") } } } return genTask to checkTask } +fun TaskContainerScope.genJsonTask(classFqName: String, fileName: String, title: String) = + genTask( + "json", listOf(classFqName), fileName.capitalize(), + "${fileName}.schema.json", title, + schemasExportLocation, schemasGenerationLocation) + +fun TaskContainerScope.genTsTask(classFqNames: List, fileName: String) = + genTask("type-script", classFqNames, fileName.capitalize(), + "${fileName}.ts", null, + tsExportLocation, tsGenerationLocation, + ) + tasks { - val genAndCheckTasks = listOf( - genTask("org.icpclive.api.tunning", "AdvancedProperties", "advanced", "ICPC live advanced settings"), - genTask("org.icpclive.cds.settings", "CDSSettings", "settings", "ICPC live settings"), + val schemaAllTasks = listOf( + genJsonTask( + "org.icpclive.api.tunning.AdvancedProperties", + "advanced", + "ICPC live advanced settings" + ), + genJsonTask( + "org.icpclive.cds.settings.CDSSettings", + "settings", + "ICPC live settings" + ) + ) + val tsAllTasks = listOf( + genTsTask( + listOf( + "org.icpclive.api.ContestInfo", + "org.icpclive.api.RunInfo", + "org.icpclive.api.Scoreboard", + "org.icpclive.api.MainScreenEvent", + "org.icpclive.api.QueueEvent", + "org.icpclive.api.AnalyticsEvent", + "org.icpclive.api.TickerEvent" + ), + "api", + ), ) - val genTasks = genAndCheckTasks.map { it.first } - val checkTasks = genAndCheckTasks.map { it.second } + val schemasGenTasks = schemaAllTasks.map { it.first } + val tsGenTasks = tsAllTasks.map { it.first } + val checkTasks = schemaAllTasks.map { it.second } + tsAllTasks.map { it.second } // Gradle for inter-project dependencies uses outgoing variants. Those are a bit hard to properly set up, so this // project just uses cross-project tasks dependencies (that from the looks of configuration cache aren't welcome, @@ -65,14 +113,28 @@ tasks { group = "build" destinationDir = schemasGatherLocation - from(genTasks) + from(schemasGenTasks) } + val generateTs = register("generateAllTs") { + group = "build" + destinationDir = tsGatherLocation - register("gen") { + from(tsGenTasks) + } + + val exportSchemas = register("exportSchemas") { destinationDir = schemasExportLocation from(generateSchemas) } + val exportTs = register("exportTs") { + destinationDir = tsExportLocation + + from(generateTs) + } + register("gen") { + dependsOn(exportSchemas, exportTs) + } check { dependsOn(checkTasks) @@ -83,7 +145,9 @@ tasks { dependencies { implementation(projects.common) implementation(libs.cli) + implementation(libs.kxs.ts.gen.core) runtimeOnly(projects.cds) + runtimeOnly(projects.backendApi) testImplementation(libs.kotlin.junit) } \ No newline at end of file diff --git a/src/schema-generator/src/main/kotlin/gen.kt b/src/schema-generator/src/main/kotlin/gen.kt deleted file mode 100644 index bc18b2bbd..000000000 --- a/src/schema-generator/src/main/kotlin/gen.kt +++ /dev/null @@ -1,147 +0,0 @@ -@file:OptIn(ExperimentalSerializationApi::class) - -package org.icpclive.generator.schema - - -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.arguments.argument -import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.options.required -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.json.* -import java.io.File - -fun PrimitiveKind.toJsonTypeName(): String = when (this) { - PrimitiveKind.BOOLEAN -> "boolean" - PrimitiveKind.BYTE -> "integer" - PrimitiveKind.CHAR -> "integer" - PrimitiveKind.DOUBLE -> "number" - PrimitiveKind.FLOAT -> "number" - PrimitiveKind.INT -> "integer" - PrimitiveKind.LONG -> "integer" - PrimitiveKind.SHORT -> "integer" - PrimitiveKind.STRING -> "string" -} - -fun SerialDescriptor.toJsonSchemaType(extras: Map = emptyMap(), extraTypeProperty: String? = null) : JsonElement { - val kind = kind - val data = when (kind) { - PolymorphicKind.OPEN -> TODO("Open polymorphic types are not supported") - SerialKind.CONTEXTUAL -> TODO("Contextual types are not supported") - PolymorphicKind.SEALED -> { - require(extraTypeProperty == null) - val typeFieldName = getElementName(0) - val contextualDescriptor = getElementDescriptor(1) - mapOf( - "oneOf" to JsonArray( - contextualDescriptor.elementDescriptors.map { - it.toJsonSchemaType(extraTypeProperty = typeFieldName) - } - ) - ) - } - is PrimitiveKind -> { - require(extraTypeProperty == null) - mapOf("type" to JsonPrimitive(kind.toJsonTypeName())) - } - SerialKind.ENUM -> { - require(extraTypeProperty == null) - mapOf("enum" to JsonArray(elementNames.map { JsonPrimitive(it) })) - } - StructureKind.CLASS -> { - if (isInline) { - return getElementDescriptor(0).toJsonSchemaType(extras, extraTypeProperty) - } - JsonObject( - mapOf( - "type" to JsonPrimitive("object"), - "properties" to JsonObject( - listOfNotNull(extraTypeProperty).associateWith { - JsonObject( - mapOf( - "const" to JsonPrimitive( - serialName - ) - ) - ) - } + - (0 until elementsCount).associate { getElementName(it) to getElementDescriptor(it).toJsonSchemaType() } - ), - "additionalProperties" to JsonPrimitive(false), - "required" to JsonArray( - (listOfNotNull(extraTypeProperty) + - (0 until elementsCount).filterNot { isElementOptional(it) } - .map { getElementName(it) }).map { JsonPrimitive(it) } - ) - ) - ) - } - StructureKind.LIST -> { - mapOf( - "type" to JsonPrimitive("array"), - "items" to getElementDescriptor(0).toJsonSchemaType() - ) - } - StructureKind.MAP -> { - val keysSerializer = getElementDescriptor(0) - val valuesSerializaer = getElementDescriptor(1) - when (keysSerializer.kind) { - PrimitiveKind.STRING -> { - JsonObject(mapOf( - "type" to JsonPrimitive("object"), - "patternProperties" to JsonObject( - mapOf(".*" to valuesSerializaer.toJsonSchemaType()) - ) - )) - } - SerialKind.ENUM -> { - JsonObject(mapOf( - "type" to JsonPrimitive("object"), - "properties" to JsonObject( - (0 until keysSerializer.elementsCount).map { - keysSerializer.getElementName(it) to valuesSerializaer.toJsonSchemaType() - }.toMap() - ) - )) - } - else -> error("Unsupported map key: $keysSerializer") - } - } - StructureKind.OBJECT -> TODO("Object types are not supported") - } - return JsonObject(extras + data) -} - -fun SerialDescriptor.toJsonSchema(title: String, path: String) : JsonElement { - return toJsonSchemaType( - extras = mapOf( - "\$schema" to JsonPrimitive("https://json-schema.org/draft/2020-12/schema"), - "\$id" to JsonPrimitive("https://github.com/icpc/live-v3/blob/main/$path"), - "title" to JsonPrimitive(title) - ) - ) -} - - -private val json = Json { - prettyPrint = true -} - -class GenCommand: CliktCommand() { - private val className by argument(help = "Class name for which schema should be generated") - private val output by option("--output", "-o", help = "File to print output").required() - private val title by option("--title", "-t", help = "Title inside schema file").required() - - override fun run() { - val serializer = serializer(Class.forName(className)).descriptor - val schema = json.encodeToString(serializer.toJsonSchema(title, output)) - File(output).printWriter().use { - it.println(schema) - } - } -} - -fun main(args: Array) { - GenCommand().main(args) -} \ No newline at end of file diff --git a/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/gen.kt b/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/gen.kt new file mode 100644 index 000000000..53834276d --- /dev/null +++ b/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/gen.kt @@ -0,0 +1,14 @@ +package org.icpclive.generator.schema + + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.subcommands + + +class MainCommand : CliktCommand() { + override fun run() {} +} + +fun main(args: Array) { + MainCommand().subcommands(JsonCommand(), TSCommand()).main(args) +} \ No newline at end of file diff --git a/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/json.kt b/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/json.kt new file mode 100644 index 000000000..0c7c60f33 --- /dev/null +++ b/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/json.kt @@ -0,0 +1,197 @@ +package org.icpclive.generator.schema + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.EmptySerializersModule +import java.io.File +import kotlin.reflect.KClass + +fun PrimitiveKind.toJsonTypeName(): String = when (this) { + PrimitiveKind.BOOLEAN -> "boolean" + PrimitiveKind.BYTE -> "number" + PrimitiveKind.CHAR -> "number" + PrimitiveKind.DOUBLE -> "number" + PrimitiveKind.FLOAT -> "number" + PrimitiveKind.INT -> "number" + PrimitiveKind.LONG -> "number" + PrimitiveKind.SHORT -> "number" + PrimitiveKind.STRING -> "string" +} + +@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class) +fun SerialDescriptor.toJsonSchemaType( + processed: MutableSet, + definitions: MutableMap, + extras: Map = emptyMap(), + extraTypeProperty: String? = null, + title: String? = null, +): JsonElement { + if (kind is PrimitiveKind) { + require(extraTypeProperty == null) + return JsonObject(mapOf("type" to JsonPrimitive((kind as PrimitiveKind).toJsonTypeName()))) + } + if (kind == SerialKind.CONTEXTUAL) { + val kclass = capturedKClass ?: error("Contextual serializer $serialName doesn't have class") + val defaultSerializer = serializer(kclass, emptyList(), isNullable) + return defaultSerializer.descriptor.toJsonSchemaType(processed, definitions, extras, extraTypeProperty) + } + if (isInline) { + return getElementDescriptor(0).toJsonSchemaType(processed, definitions, extras, extraTypeProperty) + } + // Before processing, check for recursion + val paramNamesWithTypes = elementDescriptors.map { it.serialName } + val name = serialName + if (paramNamesWithTypes.isEmpty()) "" else "<${paramNamesWithTypes.joinToString(",")}>" + val id = title ?: name + if (!processed.contains(id)) { + processed.add(id) + val data = when (kind) { + PolymorphicKind.OPEN -> TODO("Open polymorphic types are not supported") + is PrimitiveKind, SerialKind.CONTEXTUAL -> error("Already handled") + PolymorphicKind.SEALED -> { + require(extraTypeProperty == null) + val typeFieldName = getElementName(0) + val contextualDescriptor = getElementDescriptor(1) + mapOf( + "oneOf" to JsonArray( + contextualDescriptor.elementDescriptors + .sortedBy { it.serialName } + .map { + it.toJsonSchemaType( + processed, + definitions, + title = it.serialName.split(".").last(), + extraTypeProperty = typeFieldName + ) + } + ) + ) + } + + + SerialKind.ENUM -> { + require(extraTypeProperty == null) + mapOf("enum" to JsonArray(elementNames.map { JsonPrimitive(it) })) + } + + StructureKind.CLASS -> { + JsonObject( + mapOf( + "type" to JsonPrimitive("object"), + "properties" to JsonObject( + listOfNotNull(extraTypeProperty).associateWith { + JsonObject( + mapOf( + "const" to JsonPrimitive(serialName), + "default" to JsonPrimitive(serialName), + ) + ) + } + + (0 until elementsCount).associate { + getElementName(it) to + getElementDescriptor(it).toJsonSchemaType(processed, definitions) + } + ), + "additionalProperties" to JsonPrimitive(false), + "required" to JsonArray( + (listOfNotNull(extraTypeProperty) + + (0 until elementsCount).filterNot { isElementOptional(it) } + .map { getElementName(it) }).map { JsonPrimitive(it) } + ) + ) + ) + } + + StructureKind.LIST -> { + mapOf( + "type" to JsonPrimitive("array"), + "items" to getElementDescriptor(0).toJsonSchemaType(processed, definitions) + ) + } + + StructureKind.MAP -> { + val keysSerializer = getElementDescriptor(0) + val valuesSerializer = getElementDescriptor(1) + when (keysSerializer.kind) { + PrimitiveKind.STRING -> { + JsonObject( + mapOf( + "type" to JsonPrimitive("object"), + "patternProperties" to JsonObject( + mapOf( + ".*" to valuesSerializer.toJsonSchemaType( + processed, + definitions + ) + ) + ) + ) + ) + } + + SerialKind.ENUM -> { + JsonObject(mapOf( + "type" to JsonPrimitive("object"), + "properties" to JsonObject( + (0 until keysSerializer.elementsCount).associate { + keysSerializer.getElementName(it) to valuesSerializer.toJsonSchemaType( + processed, + definitions + ) + } + ) + )) + } + + else -> error("Unsupported map key: $keysSerializer") + } + } + + StructureKind.OBJECT -> TODO("Object types are not supported") + } + val content = (extras + data).toMutableMap() + if (title != null) { + content["title"] = JsonPrimitive(title) + } + definitions[id] = JsonObject(content) + } + return JsonObject(mapOf("\$ref" to JsonPrimitive("#/\$defs/$id"))) +} + +fun SerialDescriptor.toJsonSchema(title: String): JsonElement { + val definitions = mutableMapOf() + val mainSchema = toJsonSchemaType( + title = title, + processed = mutableSetOf(), + definitions = definitions, + ) + return JsonObject( + (mainSchema as JsonObject) + + mapOf( + "\$defs" to JsonObject(definitions), + ) + ) +} + + +private val json = Json { + prettyPrint = true +} + +class JsonCommand : CliktCommand(name = "json") { + private val className by option(help = "Class name for which schema should be generated") + private val output by option("--output", "-o", help = "File to print output").required() + private val title by option("--title", "-t", help = "Title inside schema file").required() + + override fun run() { + val thing = serializer(Class.forName(className)) + val serializer = thing.descriptor + val schema = json.encodeToString(serializer.toJsonSchema(title)) + File(output).printWriter().use { + it.println(schema) + } + } +} diff --git a/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/ts.kt b/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/ts.kt new file mode 100644 index 000000000..681386f80 --- /dev/null +++ b/src/schema-generator/src/main/kotlin/org/icpclive/generator/schema/ts.kt @@ -0,0 +1,43 @@ +package org.icpclive.generator.schema + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.* +import dev.adamko.kxstsgen.KxsTsGenerator +import dev.adamko.kxstsgen.core.TsDeclaration +import kotlinx.serialization.KSerializer +import kotlinx.serialization.serializer +import java.io.File + +// This is a copy-pasted generating method to work around a bug. +fun KxsTsGenerator.patchedGenerate(vararg serializers: KSerializer<*>) : String { + return serializers + .toSet() + .flatMap { serializer -> descriptorsExtractor(serializer) } + .toSet() + .flatMap { descriptor -> elementConverter(descriptor) } + .toSet() + .groupBy { element -> sourceCodeGenerator.groupElementsBy(element) } + .mapValues { (_, elements) -> + elements + .filterIsInstance() + .map { element -> sourceCodeGenerator.generateDeclaration(element) } + .filter { it.isNotBlank() } + .toSet() // workaround for https://github.com/adamko-dev/kotlinx-serialization-typescript-generator/issues/110 + .joinToString(config.declarationSeparator) + } + .values + .joinToString(config.declarationSeparator) +} + +class TSCommand : CliktCommand(name = "type-script") { + private val className by option(help = "Class name for which schema should be generated").multiple() + private val output by option("--output", "-o", help = "File to print output").required() + + override fun run() { + val tsGenerator = KxsTsGenerator() + val things = className.map { serializer(Class.forName(it)) } + File(output).printWriter().use { + it.println(tsGenerator.patchedGenerate(*things.toTypedArray())) + } + } +} diff --git a/tests/capture.spec.tsx b/tests/capture.spec.tsx index 552fa49dd..6a35d64ab 100644 --- a/tests/capture.spec.tsx +++ b/tests/capture.spec.tsx @@ -5,7 +5,7 @@ import WebSocket from "ws"; const simpleWidgets = ["queue", "scoreboard", "statistics"]; const contestConfigs = [ "config/__tests/ejudge_icpc_unfreeze/2023-voronezh", - "config/__tests/ejudge_ioi/regionalroi-lpk-2021-d1", +// "config/__tests/ejudge_ioi/regionalroi-lpk-2021-d1", "config/__tests/ejudge_ioi_virtual/mosh-2023-keldysh", "config/__tests/pcms_icpc_freeze/icpc-nef-2022-2023", "config/__tests/pcms_icpc_overrides/icpc-nef-2021-2022",