From 7edd7e47e3c20f11c276a54566f47481f4ae3b68 Mon Sep 17 00:00:00 2001 From: Andrey Vihrov Date: Mon, 14 Oct 2019 21:02:58 +0300 Subject: [PATCH 1/9] Update Latvian translation --- cms/locale/lv/LC_MESSAGES/cms.po | 346 ++++++++++++++++--------------- 1 file changed, 183 insertions(+), 163 deletions(-) diff --git a/cms/locale/lv/LC_MESSAGES/cms.po b/cms/locale/lv/LC_MESSAGES/cms.po index 2541fdea64..0c3e828a2d 100644 --- a/cms/locale/lv/LC_MESSAGES/cms.po +++ b/cms/locale/lv/LC_MESSAGES/cms.po @@ -8,18 +8,17 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: contestms@googlegroups.com\n" -"POT-Creation-Date: 2018-10-01 09:06+0100\n" -"PO-Revision-Date: 2018-10-22 22:34+0300\n" +"POT-Creation-Date: 2019-02-23 11:44+0100\n" +"PO-Revision-Date: 2019-10-14 20:48+0300\n" "Last-Translator: Andrey Vihrov \n" -"Language-Team: Latvian\n" "Language: lv\n" +"Language-Team: Latvian\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " -"2);\n" "Generated-By: Babel 2.4.0\n" -"X-Generator: Poedit 2.2\n" +"X-Generator: Poedit 2.2.1\n" msgid "N/A" msgstr "Nav pieejams" @@ -71,28 +70,27 @@ msgid "Compilation timed out" msgstr "Pārsniegts kompilēšanai atvēlētais laiks" msgid "" -"Your submission exceeded the time limit while compiling. This might be " -"caused by an excessive use of C++ templates, for example." +"Your submission exceeded the time limit while compiling. This might be caused by an " +"excessive use of C++ templates, for example." msgstr "" -"Jūsu iesūtījums kompilēšanas laikā ir pārsniedzis laika ierobežojumu. To, " -"piemēram, var būt izraisījis pārliecīgs C++ šablonu lietojums." +"Jūsu iesūtījums kompilēšanas laikā ir pārsniedzis laika ierobežojumu. To, piemēram, " +"var būt izraisījis pārliecīgs C++ šablonu lietojums." #, python-format msgid "" -"Compilation killed with signal %s (could be triggered by violating memory " -"limits)" +"Compilation killed with signal %s (could be triggered by violating memory limits)" msgstr "" -"Kompilēšana pārtraukta ar signālu %s (to var būt izraisījusi atmiņas " -"ierobežojumu pārkāpšana)" +"Kompilēšana pārtraukta ar signālu %s (to var būt izraisījusi atmiņas ierobežojumu " +"pārkāpšana)" msgid "" -"Your submission was killed with the specified signal. Among other things, " -"this might be caused by exceeding the memory limit for the compilation, and " -"in turn by an excessive use of C++ templates, for example." +"Your submission was killed with the specified signal. Among other things, this " +"might be caused by exceeding the memory limit for the compilation, and in turn by " +"an excessive use of C++ templates, for example." msgstr "" -"Jūsu iesūtījuma izpilde ir pārtraukta ar norādīto signālu. To, cita starpā, " -"var būt izraisījusi atmiņas ierobežojuma pārsniegšana kompilēšanas laikā, " -"ko, savukārt, var būt izsaucis pārliecīgs C++ šablonu lietojums." +"Jūsu iesūtījuma izpilde ir pārtraukta ar norādīto signālu. To, cita starpā, var būt " +"izraisījusi atmiņas ierobežojuma pārsniegšana kompilēšanas laikā, ko, savukārt, var " +"būt izsaucis pārliecīgs C++ šablonu lietojums." msgid "Output is correct" msgstr "Izvaddati ir pareizi" @@ -120,8 +118,7 @@ msgid "Evaluation didn't produce file %s" msgstr "Vērtēšanas laikā netika izveidota datne %s" msgid "Your submission ran, but did not write on the correct output file" -msgstr "" -"Jūsu iesūtījums tika izpildīts, bet neierakstīja pareizajā izvada datnē" +msgstr "Jūsu iesūtījums tika izpildīts, bet neierakstīja pareizajā izvada datnē" msgid "Execution timed out" msgstr "Pārsniegts izpildes laiks" @@ -133,38 +130,34 @@ msgid "Execution timed out (wall clock limit exceeded)" msgstr "Pārsniegts izpildes laiks (pārsniegts reālā laika ierobežojums)" msgid "" -"Your submission used too much total time. This might be triggered by " -"undefined code, or buffer overflow, for example. Note that in this case the " -"CPU time visible in the submission details might be much smaller than the " -"time limit." +"Your submission used too much total time. This might be triggered by undefined " +"code, or buffer overflow, for example. Note that in this case the CPU time visible " +"in the submission details might be much smaller than the time limit." msgstr "" "Jūsu iesūtījums kopumā ir patērējis pārāk daudz laika. Tas var būt noticis, " -"piemēram, nedefinētas koda uzvedības vai bufera pārpildīšanās dēļ. Šādos " -"gadījumos iesūtījuma detaļās norādītais procesora laiks var būt ievērojami " -"mazāks nekā izpildes laika ierobežojums." +"piemēram, nedefinētas koda uzvedības vai bufera pārpildīšanās dēļ. Šādos gadījumos " +"iesūtījuma detaļās norādītais procesora laiks var būt ievērojami mazāks nekā " +"izpildes laika ierobežojums." msgid "Execution killed (could be triggered by violating memory limits)" -msgstr "" -"Izpilde pārtraukta (to var būt izraisījusi atmiņas ierobežojumu neievērošana)" +msgstr "Izpilde pārtraukta (to var būt izraisījusi atmiņas ierobežojumu neievērošana)" msgid "" -"The evaluation was killed by a signal. Among other things, this might be " -"caused by exceeding the memory limit. Note that if this is the reason, the " -"memory usage visible in the submission details is the usage before the " -"allocation that caused the signal." +"The evaluation was killed by a signal. Among other things, this might be caused by " +"exceeding the memory limit. Note that if this is the reason, the memory usage " +"visible in the submission details is the usage before the allocation that caused " +"the signal." msgstr "" "Vērtēšana tika pārtraukta ar signālu. Citu iemeslu starpā, to varētu būt " -"izraisījusi atmiņas ierobežojuma pārsniegšana. Šajā gadījumā atmiņas " -"patēriņš, kas ir redzams iesūtījuma detaļās, atbilst stāvoklim pirms signālu " -"izraisījušās atmiņas iedalīšanas." +"izraisījusi atmiņas ierobežojuma pārsniegšana. Šajā gadījumā atmiņas patēriņš, kas " +"ir redzams iesūtījuma detaļās, atbilst stāvoklim pirms signālu izraisījušās atmiņas " +"iedalīšanas." msgid "Execution failed because the return code was nonzero" msgstr "Izpilde ir nesekmīga, jo atgriešanās kods nav nulle" -msgid "" -"Your submission failed because it exited with a return code different from 0." -msgstr "" -"Jūsu iesūtījums ir nesekmīgs, jo tas beidzās ar nenulles atgriešanās kodu." +msgid "Your submission failed because it exited with a return code different from 0." +msgstr "Jūsu iesūtījums ir nesekmīgs, jo tas beidzās ar nenulles atgriešanās kodu." msgid "Execution completed successfully" msgstr "Izpilde beigusies sekmīgi" @@ -183,8 +176,8 @@ msgid "" "Subject must be at most %(max_subject_length)d characters, content at most " "%(max_text_length)d." msgstr "" -"Temats var būt ne vairāk kā %(max_subject_length)d simbolus garš, saturs — " -"ne vairāk kā %(max_text_length)d." +"Temats var būt ne vairāk kā %(max_subject_length)d simbolus garš, saturs — ne " +"vairāk kā %(max_text_length)d." msgid "contest-token" msgstr "sacensību žetons" @@ -291,8 +284,7 @@ msgstr "Pārāk daudz drukas uzdevumu!" #, python-format msgid "You have reached the maximum limit of at most %d print jobs." -msgstr "" -"Jūs esat sasniedzis maksimāli pieļaujamo drukas uzdevumu ierobežojumu (%d)." +msgstr "Jūs esat sasniedzis maksimāli pieļaujamo drukas uzdevumu ierobežojumu (%d)." msgid "Invalid format!" msgstr "Nepareizs formāts!" @@ -320,18 +312,14 @@ msgid "Your request has been discarded because you have no tokens available." msgstr "Jūsu pieprasījums ir noraidīts, jo jums nav žetonu." msgid "" -"Your request has been discarded because you already used a token on that " -"submission." -msgstr "" -"Jūsu pieprasījums ir noraidīts, jo jūs jau izmantojāt žetonu šim iesūtījumam." +"Your request has been discarded because you already used a token on that submission." +msgstr "Jūsu pieprasījums ir noraidīts, jo jūs jau izmantojāt žetonu šim iesūtījumam." msgid "Question received" msgstr "Jautājums saņemts" -msgid "" -"Your question has been received, you will be notified when it is answered." -msgstr "" -"Jūsu jautājums saņemts. Tiklīdz uz to būs atbildēts, jums tiks paziņots." +msgid "Your question has been received, you will be notified when it is answered." +msgstr "Jūsu jautājums saņemts. Tiklīdz uz to būs atbildēts, jums tiks paziņots." msgid "Print job received" msgstr "Drukas uzdevums saņemts" @@ -357,6 +345,9 @@ msgstr "Vērtē…" msgid "Evaluated" msgstr "Izpildīts" +msgid "status" +msgstr "statuss" + msgid "Token request received" msgstr "Žetona pieprasījums saņemts" @@ -382,35 +373,29 @@ msgid "Too many submissions!" msgstr "Pārāk daudz iesūtījumu!" #, python-format -msgid "" -"You have reached the maximum limit of at most %d submissions among all tasks." +msgid "You have reached the maximum limit of at most %d submissions among all tasks." msgstr "" -"Jūs esat sasniedzis maksimāli pieļaujamo iesūtījumu skaitu pa visiem " -"uzdevumiem (%d)." +"Jūs esat sasniedzis maksimāli pieļaujamo iesūtījumu skaitu pa visiem uzdevumiem " +"(%d)." #, python-format -msgid "" -"You have reached the maximum limit of at most %d submissions on this task." +msgid "You have reached the maximum limit of at most %d submissions on this task." msgstr "" -"Jūs esat sasniedzis maksimāli pieļaujamo iesūtījumu skaitu šim uzdevumam " -"(%d)." +"Jūs esat sasniedzis maksimāli pieļaujamo iesūtījumu skaitu šim uzdevumam (%d)." msgid "Submissions too frequent!" msgstr "Pārāk bieži iesūtījumi!" #, python-format -msgid "" -"Among all tasks, you can submit again after %d seconds from last submission." +msgid "Among all tasks, you can submit again after %d seconds from last submission." msgstr "" "Jebkura uzdevuma risinājumu jūs drīkstat iesūtīt %d sekundes pēc iepriekšējā " "iesūtījuma." #, python-format -msgid "" -"For this task, you can submit again after %d seconds from last submission." +msgid "For this task, you can submit again after %d seconds from last submission." msgstr "" -"Šī uzdevuma risinājumu jūs drīkstat iesūtīt %d sekundes pēc iepriekšējā " -"iesūtījuma." +"Šī uzdevuma risinājumu jūs drīkstat iesūtīt %d sekundes pēc iepriekšējā iesūtījuma." msgid "Invalid archive format!" msgstr "Nepareizs arhīva formāts!" @@ -447,8 +432,7 @@ msgstr "Pārāk bieži testi!" #, python-format msgid "Among all tasks, you can test again after %d seconds from last test." -msgstr "" -"Jebkuru uzdevumu jūs drīkstat testēt %d sekundes pēc iepriekšējā testa." +msgstr "Jebkuru uzdevumu jūs drīkstat testēt %d sekundes pēc iepriekšējā testa." #, python-format msgid "For this task, you can test again after %d seconds from last test." @@ -509,11 +493,9 @@ msgstr "Atteikties" #, python-format msgid "" -"Logged in as %(first_name)s %(last_name)s " -"(%(username)s)" +"Logged in as %(first_name)s %(last_name)s (%(username)s)" msgstr "" -"Pieteicies kā %(first_name)s %(last_name)s " -"(%(username)s)" +"Pieteicies kā %(first_name)s %(last_name)s (%(username)s)" msgid "Failed to log in." msgstr "Pieteikšanās kļūme." @@ -533,6 +515,12 @@ msgstr "Parole" msgid "Login" msgstr "Pieteikties" +msgid "Don't have an account?" +msgstr "Nav lietotāja konta?" + +msgid "Register" +msgstr "Reģistrēties" + msgid "New message" msgstr "Jauns ziņojums" @@ -601,10 +589,8 @@ msgid "Standard Template Library" msgstr "Standarta šablonu bibliotēka (STL)" msgid "" -"The main Java class of the solution should have exactly the same name as the " -"task." -msgstr "" -"Risinājuma galvenās Java klases nosaukumam ir jāsakrīt ar uzdevuma nosaukumu." +"The main Java class of the solution should have exactly the same name as the task." +msgstr "Risinājuma galvenās Java klases nosaukumam ir jāsakrīt ar uzdevuma nosaukumu." msgid "Submission details for compilation" msgstr "Iesūtījuma kompilēšanas detaļas" @@ -626,20 +612,20 @@ msgid "An error occured while the server was handling your request." msgstr "Serverim apstrādājot jūsu pieprasījumu, ir radusies kļūme." msgid "" -"Note that attempts to tamper with Contest Management System (such as probing " -"the server with customized URLs) may be considered cheating and may lead to " +"Note that attempts to tamper with Contest Management System (such as probing the " +"server with customized URLs) may be considered cheating and may lead to " "disqualification." msgstr "" -"Iedarbošanās uz testēšanas sistēmu neatļautā veidā (piemēram, mēģinājums " -"izmantot modificētas pieprasījumu adreses) var tikt uzskatīta par pārkāpumu " -"un var beigties ar diskvalifikāciju." +"Iedarbošanās uz testēšanas sistēmu neatļautā veidā (piemēram, mēģinājums izmantot " +"modificētas pieprasījumu adreses) var tikt uzskatīta par pārkāpumu un var beigties " +"ar diskvalifikāciju." msgid "" "If you encountered this error during normal usage, please notify the contest " "administrators." msgstr "" -"Ja jūs saskārāties ar šo kļūdu parastā situācijā, lūdzu, informējiet " -"sacensību administratorus." +"Ja jūs saskārāties ar šo kļūdu parastā situācijā, lūdzu, informējiet sacensību " +"administratorus." msgid "General information" msgstr "Vispārīga informācija" @@ -669,16 +655,14 @@ msgid "The analysis mode hasn't started yet." msgstr "Analīzes režīms vēl nav sācies." #, python-format -msgid "" -"The analysis mode will start at %(start_time)s and will end at %(stop_time)s." +msgid "The analysis mode will start at %(start_time)s and will end at %(stop_time)s." msgstr "Analīzes režīms sāksies %(start_time)s un beigsies %(stop_time)s." msgid "The analysis mode is currently running." msgstr "Analīzes režīms ir aktīvs." #, python-format -msgid "" -"The analysis mode started at %(start_time)s and will end at %(stop_time)s." +msgid "The analysis mode started at %(start_time)s and will end at %(stop_time)s." msgstr "Analīzes režīms ir sācies %(start_time)s un beigsies %(stop_time)s." msgid "The analysis mode has already ended." @@ -693,39 +677,35 @@ msgstr "Jums ir neierobežots skaits žetonu." msgid "You can see the detailed result of a submission by using a token on it." msgstr "" -"Detalizētus iesūtījuma rezultātus jūs varēsiet aplūkot, ja izmantosiet " -"žetonu." +"Detalizētus iesūtījuma rezultātus jūs varēsiet aplūkot, ja izmantosiet žetonu." msgid "" -"Your score for each task will be the maximum among the tokened submissions " -"and the last one." +"Your score for each task will be the maximum among the tokened submissions and the " +"last one." msgstr "" -"Katram uzdevumam iegūtais punktu skaits būs lielākais starp iesūtījumiem, " -"kuriem izmantoti žetoni, un pēdējo iesūtīto." +"Katram uzdevumam iegūtais punktu skaits būs lielākais starp iesūtījumiem, kuriem " +"izmantoti žetoni, un pēdējo iesūtīto." msgid "You have a distinct set of tokens for each task." msgstr "Jums ir atsevišķi žetoni katram uzdevumam." #, python-format -msgid "" -"You can find the rules for the %(type_pl)s on each task's description page." -msgstr "" -"Katra uzdevuma apraksta lapā ir atrodami %(type_pl)s izmantošanas noteikumi." +msgid "You can find the rules for the %(type_pl)s on each task's description page." +msgstr "Katra uzdevuma apraksta lapā ir atrodami %(type_pl)s izmantošanas noteikumi." msgid "You have a set of tokens shared among all tasks." msgstr "Žetoni ir kopīgi visiem uzdevumiem." msgid "" -"You have two types of tokens: a set of contest-tokens shared among " -"all tasks and a distinct set of task-tokens for each task." +"You have two types of tokens: a set of contest-tokens shared among all " +"tasks and a distinct set of task-tokens for each task." msgstr "" -"Ir divu veidu žetoni: sacensību žetoni, kas derīgi visiem " -"uzdevumiem, un uzdevumu žetoni, kas derīgi katram atsevišķam " -"uzdevumam." +"Ir divu veidu žetoni: sacensību žetoni, kas derīgi visiem uzdevumiem, un " +"uzdevumu žetoni, kas derīgi katram atsevišķam uzdevumam." msgid "" -"You can see the detailed result of a submission by using two tokens on it, " -"one of each type." +"You can see the detailed result of a submission by using two tokens on it, one of " +"each type." msgstr "" "Detalizētus iesūtījuma rezultātus jūs varēsiet aplūkot, ja izmantosiet divus " "žetonus — pa vienam no katra žetonu veida." @@ -733,33 +713,31 @@ msgstr "" #, python-format msgid "You can submit at most %(submissions)s solutions during this contest." msgstr "" -"Šo sacensību laikā jūs drīkstat iesūtīt ne vairāk kā %(submissions)s " -"risinājumus." +"Šo sacensību laikā jūs drīkstat iesūtīt ne vairāk kā %(submissions)s risinājumus." #, python-format msgid "You can submit at most %(user_tests)s user tests during this contest." msgstr "" -"Šo sacensību laikā jūs drīkstat iesūtīt ne vairāk kā %(user_tests)s " -"lietotāja testus." +"Šo sacensību laikā jūs drīkstat iesūtīt ne vairāk kā %(user_tests)s lietotāja " +"testus." #, python-format msgid "" -"Every user is allowed to compete (i.e. submit solutions) for a uninterrupted " -"time frame of %(per_user_time)s." +"Every user is allowed to compete (i.e. submit solutions) for a uninterrupted time " +"frame of %(per_user_time)s." msgstr "" "Katram lietotājam ir atļauts sacensties (t.i., iesūtīt risinājumus) " "%(per_user_time)s ilgā nepārtrauktā laikaposmā." msgid "As soon as the contest starts you can choose to start your time frame." -msgstr "" -"Tiklīdz sacensības sāksies, jūs varēsiet izvēlēties sava laikaposma sākumu." +msgstr "Tiklīdz sacensības sāksies, jūs varēsiet izvēlēties sava laikaposma sākumu." msgid "" -"Once you start, you can submit solutions until the end of the time frame or " -"until the end of the contest, whatever comes first." +"Once you start, you can submit solutions until the end of the time frame or until " +"the end of the contest, whatever comes first." msgstr "" -"Tiklīdz jūs sāksiet, jūs varēsiet iesūtīt risinājumus līdz izvēlētā " -"laikaposma vai sacensību beigām (kas iestāsies pirmās)." +"Tiklīdz jūs sāksiet, jūs varēsiet iesūtīt risinājumus līdz izvēlētā laikaposma vai " +"sacensību beigām (kas iestāsies pirmās)." msgid "By clicking on the button below you can start your time frame." msgstr "Nospiežot zemāk esošo pogu, jūs varat sākt savu laikaposmu." @@ -769,15 +747,14 @@ msgid "You started your time frame at %(start_time)s." msgstr "Jūs sākāt savu laikaposmu %(start_time)s." msgid "" -"You can submit solutions until the end of the time frame or until the end of " -"the contest, whatever comes first." +"You can submit solutions until the end of the time frame or until the end of the " +"contest, whatever comes first." msgstr "" -"Jūs varat iesūtīt risinājumus līdz izvēlētā laikaposma vai sacensību beigām " -"(kas iestāsies pirmās)." +"Jūs varat iesūtīt risinājumus līdz izvēlētā laikaposma vai sacensību beigām (kas " +"iestāsies pirmās)." #, python-format -msgid "" -"You started your time frame at %(start_time)s and you already finished it." +msgid "You started your time frame at %(start_time)s and you already finished it." msgstr "Jūs sākāt savu laikaposmu %(start_time)s, un tas ir noslēdzies." msgid "There's nothing you can do now." @@ -824,19 +801,18 @@ msgstr "Drukāt" #, python-format msgid "" -"You can print %(remaining_jobs)s more text or PDF files of up to " -"%(max_pages)s pages each." +"You can print %(remaining_jobs)s more text or PDF files of up to %(max_pages)s " +"pages each." msgstr "" -"Jūs drīkstat izdrukāt vēl %(remaining_jobs)s teksta vai PDF datnes (katru ne " -"garāku par %(max_pages)s lappusēm)." +"Jūs drīkstat izdrukāt vēl %(remaining_jobs)s teksta vai PDF datnes (katru ne garāku " +"par %(max_pages)s lappusēm)." #, python-format msgid "" -"You can print %(remaining_jobs)s more text files of up to %(max_pages)s " -"pages each." +"You can print %(remaining_jobs)s more text files of up to %(max_pages)s pages each." msgstr "" -"Jūs drīkstat izdrukāt vēl %(remaining_jobs)s teksta datnes (katru ne garāku " -"par %(max_pages)s lappusēm)." +"Jūs drīkstat izdrukāt vēl %(remaining_jobs)s teksta datnes (katru ne garāku par " +"%(max_pages)s lappusēm)." msgid "File (text or PDF)" msgstr "Datne (teksta vai PDF)" @@ -847,11 +823,9 @@ msgstr "Datne (teksta)" msgid "Submit" msgstr "Iesūtīt" -msgid "" -"You cannot print anything any more as you have used up your printing quota." +msgid "You cannot print anything any more as you have used up your printing quota." msgstr "" -"Jūs vairs neko nevarat izdrukāt, jo jau esat izlietojuši atļauto lappušu " -"skaitu." +"Jūs vairs neko nevarat izdrukāt, jo jau esat izlietojuši atļauto lappušu skaitu." msgid "Previous print jobs" msgstr "Iepriekšējie drukas uzdevumi" @@ -874,6 +848,52 @@ msgstr "Sagatavojas…" msgid "no print jobs yet" msgstr "pagaidām nav drukas uzdevumu" +msgid "The passwords do not match!" +msgstr "Paroles nesakrīt!" + +msgid "This username is already taken, please choose a different one." +msgstr "Šis lietotājvārds jau ir aizņemts; lūdzu, izvēlieties citu." + +msgid "New user" +msgstr "Jauns lietotājs" + +msgid "Please fill in the fields to register" +msgstr "Lai reģistrētos, lūdzu, aizpildiet laukus" + +msgid "First name" +msgstr "Vārds" + +msgid "Last name" +msgstr "Uzvārds" + +msgid "E-mail" +msgstr "E-pasts" + +msgid "Representing" +msgstr "Pārstāv" + +#, python-format +msgid "Must be one character or more." +msgid_plural "Must be %(min_length)s characters or more." +msgstr[0] "Jābūt vismaz %(min_length)s simbolam." +msgstr[1] "Jābūt vismaz %(min_length)s simboliem." +msgstr[2] "Jābūt vismaz %(min_length)s simbolu." + +msgid "Confirm password" +msgstr "Apstipriniet paroli" + +msgid "The user was created successfully!" +msgstr "Lietotājs sekmīgi izveidots!" + +msgid "Your username is:" +msgstr "Jūsu lietotājvārds ir:" + +msgid "The password you chose was stored securely." +msgstr "Jūsu izvēlētā parole tika saglabāta drošā veidā." + +msgid "Back to login" +msgstr "Atpakaļ uz pieteikšanos" + msgid "Compilation output" msgstr "Kompilēšanas izvads" @@ -910,6 +930,21 @@ msgstr "Gaidiet…" msgid "No tokens" msgstr "Nav žetonu" +msgid "Public score" +msgstr "Publiski redzamais rezultāts" + +msgid "Total score" +msgstr "Kopējais rezultāts" + +msgid "Score" +msgstr "Rezultāts" + +msgid "Token" +msgstr "Žetons" + +msgid "no submissions" +msgstr "nav iesūtījumu" + #, python-format msgid "%(name)s (%(short_name)s) description" msgstr "%(name)s (%(short_name)s) apraksts" @@ -923,12 +958,10 @@ msgstr "Lejuplādēt uzdevuma formulējumu" msgid "" "The statement for this task is available in multiple versions, in different " "languages." -msgstr "" -"Šī uzdevuma formulējums ir pieejums vairākās versijās, dažādās valodās." +msgstr "Šī uzdevuma formulējums ir pieejams vairākās versijās, dažādās valodās." msgid "You can see (and download) all of them using the list on the right." -msgstr "" -"Jūs varat aplūkot (un lejuplādēt) visas tās, izmantojot sarakstu labajā pusē." +msgstr "Jūs varat aplūkot (un lejuplādēt) visas tās, izmantojot sarakstu labajā pusē." msgid "Some suggested translations follow." msgstr "Zemāk seko daži ieteiktie tulkojumi." @@ -960,12 +993,12 @@ msgid "" "You can find the rules for the %(type_pl)s on the contest overview page." msgstr "" -"Jūs varat atrast %(type_pl)s noteikumus sacensību pārskata lapā." +"Jūs varat atrast %(type_pl)s noteikumus sacensību " +"pārskata lapā." msgid "" -"Remember that to see the detailed result of a submission you need to use " -"both a contest-token and a task-token." +"Remember that to see the detailed result of a submission you need to use both a " +"contest-token and a task-token." msgstr "" "Lai aplūkotu iesūtījuma detalizētus rezultātus, nepieciešams izmantot gan " "sacensību, gan uzdevuma žetonu." @@ -1027,8 +1060,7 @@ msgstr "Šobrīd šim uzdevumam jums ir pieejami %(tokens)s žetoni." #, python-format msgid "But you have to wait until %(expiration_time)s to use them." -msgstr "" -"Bet, lai tos izmantotu, nepieciešams pagaidīt līdz %(expiration_time)s." +msgstr "Bet, lai tos izmantotu, nepieciešams pagaidīt līdz %(expiration_time)s." #, python-format msgid "You will receive a new token at %(gen_time)s." @@ -1044,23 +1076,11 @@ msgstr "Šobrīd jums šim uzdevumam nav žetonu." msgid "But you will have to wait until %(expiration_time)s to use it." msgstr "Bet, lai to izmantotu, nepieciešams pagaidīt līdz %(expiration_time)s." -msgid "Public score" -msgstr "Publiski redzamais rezultāts" - -msgid "Total score" -msgstr "Kopējais rezultāts" - -msgid "Score" -msgstr "Rezultāts" - -msgid "Official" -msgstr "Ieskaitīts" - -msgid "Token" -msgstr "Žetons" +msgid "Unofficial submissions" +msgstr "Neoficiāli iesūtījumi" -msgid "no submissions yet" -msgstr "pagaidām nav iesūtījumu" +msgid "Official submissions" +msgstr "Oficiāli iesūtījumi" msgid "Submission details" msgstr "Iesūtījuma detaļas" From d9a9f89a42831b7e0866b5e2c6f5a4ad4ddd1b91 Mon Sep 17 00:00:00 2001 From: Andrey Vihrov Date: Mon, 28 Oct 2019 22:20:59 +0200 Subject: [PATCH 2/9] Update LIO favicon --- cms/server/static/favicon.ico | Bin 16958 -> 16958 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/cms/server/static/favicon.ico b/cms/server/static/favicon.ico index 66d3497a0588912e30d1a9bf8e022205eb3beb80..4d6540649663aeb6d1a33d93171d28d27f2a4242 100644 GIT binary patch literal 16958 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 OBDcpkK5GMq%IANIt&Ix+ From 9e5c87f86f03183d6b779ef2a06c157082e22679 Mon Sep 17 00:00:00 2001 From: Andrey Vihrov Date: Sun, 24 Nov 2019 15:54:43 +0200 Subject: [PATCH 3/9] Fine-tune OpenJDK evaluation parameters --- cms/grading/languages/java_jdk.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cms/grading/languages/java_jdk.py b/cms/grading/languages/java_jdk.py index dbc6ba6eaf..25269b1177 100644 --- a/cms/grading/languages/java_jdk.py +++ b/cms/grading/languages/java_jdk.py @@ -79,6 +79,10 @@ def get_evaluation_commands( # executable_filename is a jar file, main is the name of # the main java class return [["/usr/bin/java", "-Deval=true", "-Xmx512M", "-Xss64M", + # Additional parameters to increase speed and stability. Tuned for Java 8. + # See also: https://wiki.ioinformatics.org/wiki/HostingAnIOI/TechnicalChecklist#Determinism + "-Xbatch", "-XX:+UseSerialGC", "-XX:-TieredCompilation", + "-XX:CICompilerCount=1", "-XX:CompileThreshold=2000", "-XX:-UsePerfData", "-cp", executable_filename, main] + args] else: unzip_command = ["/usr/bin/unzip", executable_filename] From 28fee3b9e5a4809908b44810fc5bb0771e5f80af Mon Sep 17 00:00:00 2001 From: PPakalns Date: Thu, 6 Dec 2018 13:20:48 +0200 Subject: [PATCH 4/9] Lio contest loader --- cmscontrib/loaders/__init__.py | 4 +- cmscontrib/loaders/lio.py | 314 +++++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 cmscontrib/loaders/lio.py diff --git a/cmscontrib/loaders/__init__.py b/cmscontrib/loaders/__init__.py index 337403a0b7..55238c189b 100644 --- a/cmscontrib/loaders/__init__.py +++ b/cmscontrib/loaders/__init__.py @@ -18,13 +18,15 @@ from .italy_yaml import YamlLoader from .polygon import PolygonTaskLoader, PolygonUserLoader, PolygonContestLoader from .tps import TpsTaskLoader +from .lio import LioTaskLoader, LioContestLoader LOADERS = dict( (loader_class.short_name, loader_class) for loader_class in [ YamlLoader, PolygonTaskLoader, PolygonUserLoader, PolygonContestLoader, - TpsTaskLoader + TpsTaskLoader, + LioTaskLoader, LioContestLoader, ] ) diff --git a/cmscontrib/loaders/lio.py b/cmscontrib/loaders/lio.py new file mode 100644 index 0000000000..db98f865a7 --- /dev/null +++ b/cmscontrib/loaders/lio.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 + +# Contest Management System - http://cms-dev.github.io/ +# Copyright © 2018 Pēteris Pakalns +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import os +import re +import logging +import yaml +import zipfile +import datetime +import subprocess + +from cms import config +from cms.db import Contest, Dataset, Task, Statement, Testcase, Manager +from .base_loader import ContestLoader, TaskLoader +from cms import TOKEN_MODE_DISABLED, TOKEN_MODE_FINITE, TOKEN_MODE_INFINITE +from cmscommon.constants import \ + SCORE_MODE_MAX, SCORE_MODE_MAX_SUBTASK, SCORE_MODE_MAX_TOKENED_LAST +from .italy_yaml import load_yaml_from_path, make_timedelta +from datetime import timedelta + +logger = logging.getLogger(__name__) + + +def set_if_present(src_dict, trg_dict, key, conv=None): + if key in src_dict: + if conv: + trg_dict[key] = conv(src_dict[key]) + else: + trg_dict[key] = src_dict[key] + + +class LioTaskLoader(TaskLoader): + + short_name = "Lio-task" + description = "Latvian Informatics Olympiad task loader" + + def __init__(self, path, file_cacher): + super().__init__(path, file_cacher) + self.task_dir = os.path.dirname(self.path) + self.conf = load_yaml_from_path(self.path) + + + @staticmethod + def detect(path): + # TODO: Support auto detection + return False + + + def parse_point_file(self, point_path): + """ + Parse point file with the following format for each line + {from group}-{till group} {points for each group} {comment} + + return ({group: points}): Dictionary of points per group + """ + with open(point_path, "rt", encoding="utf-8") as f: + content = [line.strip() for line in f.readlines()] + points_per_group = dict() + for line in content: + vars = line.replace("-", " ").split() + a = int(vars[0]) + b = int(vars[1]) + points = int(vars[2]) + for group in range(a, b+1): + if group in points_per_group: + logger.critical("Duplicated groups in point file") + raise ValueError + points_per_group[group] = points + for group in range(len(points_per_group)): + if group not in points_per_group: + logger.critical("Missing group from point file") + raise ValueError + return points_per_group + + + def get_task(self, get_statement): + args = { + 'name': self.conf['name'], + 'title': self.conf['title'], + } + name = args['name'] + + logger.info(f"Loading parameters for task f{name}") + + if get_statement: + args['statements'] = {} + for statement in self.conf.get('statements', []): + path, lang = statement + logger.info(f"Loading statement: {statement}") + digest = self.file_cacher.put_file_from_path( + os.path.join(self.task_dir, path), + f"Statement for task {name} (lang: {lang})", + ) + args['statements'][lang] = Statement(lang, digest) + if args['statements']: + args['primary_statements'] = self.conf.get('primary_statements', ["lv"]) + + args['submission_format'] = [f"{name}.%l"] + + set_if_present(self.conf, args, 'score_precision') + + score_mode = self.conf.get('score_mode', SCORE_MODE_MAX_TOKENED_LAST) + if score_mode in [SCORE_MODE_MAX, SCORE_MODE_MAX_SUBTASK, SCORE_MODE_MAX_TOKENED_LAST]: + args['score_mode'] = score_mode + else: + logger.critical("Unknown score mode provided") + raise ValueError + + set_if_present(self.conf, args, 'max_submission_number') + set_if_present(self.conf, args, 'max_user_test_number') + set_if_present(self.conf, args, 'min_submission_interval', make_timedelta) + set_if_present(self.conf, args, 'min_user_test_interval', make_timedelta) + + task = Task(**args) + + args = {} + args["task"] = task + args["description"] = self.conf.get("version", "Default") + args["autojudge"] = False + + args['time_limit'] = float(self.conf.get('time_limit', 2)) + # Memory limit in MiB + args['memory_limit'] = self.conf.get('memory_limit', 256) * 1024**2 + + # Builds the parameters that depend on the task type + args["managers"] = {} + + # By default use standard input, output + input_filename = self.conf.get("input_filename", "") + output_filename = self.conf.get("output_filename", "") + + # No grader support + compilation_param = "alone" + + if 'checker' in self.conf: + logger.info("Checker found, compiling") + checker_src = os.path.join(self.task_dir, self.conf['checker']) + checker_exe = os.path.join(os.path.dirname(checker_src), "checker") + if config.installed: + testlib_path = "/usr/local/include/cms" + else: + testlib_path = os.path.join(os.path.dirname(__file__), "polygon") + code = subprocess.call(["g++", "-x", "c++", "-O2", "-static", + "-DCMS", "-I", testlib_path, + "-o", checker_exe, checker_src]) + if code != 0: + logger.critical("Could not compile checker") + return None + digest = self.file_cacher.put_file_from_path( + checker_exe, "Checker for task {name}" + ) + args["managers"]["checker"] = Manager("checker", digest) + evaluation_param = "comparator" + else: + evaluation_param = "diff" + + point_file = os.path.join(self.task_dir, self.conf.get('point_file', 'punkti.txt')) + points_per_group = self.parse_point_file(point_file) + if sum(points_per_group.values()) != 100: + logger.critical("Points for all groups doesn't sum up to 100") + return None + + args["score_type"] = self.conf.get("score_type", "GroupMin") + args["score_type_parameters"] = \ + [[points_per_group[i], f"{i:03}"] for i in range(len(points_per_group))] + args["task_type"] = "Batch" + args["task_type_parameters"] = \ + [compilation_param, + [input_filename, output_filename], + evaluation_param] + public_groups = self.conf.get('public_groups', [0, 1]) + + args["testcases"] = {} + tests_per_group = [0] * len(points_per_group) + test_zip = os.path.join(self.task_dir, self.conf.get('test_archive', 'testi.zip')) + with zipfile.ZipFile(test_zip) as zip: + + # Collect and organize test files from zip archive + matcher = re.compile(r"\.(i|o)(\d+)([a-z]*)$") + test_files = {} + for test_filename in zip.namelist(): + if '/' in test_filename or '\\' in test_filename: + logger.critical("Test zip archive contains a directory") + return None + match = matcher.search(test_filename) + if not match: + logger.critical(f"Unsupported file in test archive f{name}") + return None + + is_input = match.group(1) == 'i' + group = int(match.group(2)) + test_in_group = match.group(3) + + if group not in test_files: + test_files[group] = {} + if test_in_group not in test_files[group]: + test_files[group][test_in_group] = {} + + testcase = test_files[group][test_in_group] + testcase['input' if is_input else 'output'] = test_filename + + # Extract them in correct order and update subtask list and testcases + for group in sorted(test_files.keys()): + for test_in_group in sorted(test_files[group].keys()): + tests_per_group[group] += 1 + testcase = test_files[group][test_in_group] + if 'input' not in testcase or 'output' not in testcase: + logger.critical(f"Input or output file not found for test {group}{test_in_group}") + return None + # TODO: Convert windows newlines to unix newlines (dos2unix) + with zip.open(testcase['input'], 'r') as input_file: + input_digest = self.file_cacher.put_file_from_fobj( + input_file, + f"Input {testcase['input']} for task {task.name}") + with zip.open(testcase['output'], 'r') as output_file: + output_digest = self.file_cacher.put_file_from_fobj( + output_file, + f"Output {testcase['output']} for task {task.name}") + codename = f"{group:03}{test_in_group}" + args["testcases"][codename] = \ + Testcase(codename, group in public_groups, input_digest, output_digest) + + for i in range(len(tests_per_group)): + if tests_per_group[i] == 0: + logger.critical(f"No testcases for group {i}") + return None + + task.active_dataset = Dataset(**args) + + logger.info("Task parameters loaded.") + return task + + + def task_has_changed(self): + # TODO: Detect if the task has been changed since its last import + # With temporary files and checking if some settings + # and/or file last modification time has changed + return True + + +class LioContestLoader(ContestLoader): + + short_name = "Lio-contest" + description = "Latvian Informatics Olympiad contest loader" + + def __init__(self, path, file_cacher): + super().__init__(path, file_cacher) + self.contest_dir = os.path.dirname(self.path) + self.conf = load_yaml_from_path(self.path) + + @staticmethod + def detect(path): + # TODO: Support auto detection + return False + + def get_contest(self): + args = { + 'name': self.conf['name'], + 'description': self.conf['description'], + } + + args['languages'] = self.conf.get('languages', + ["C11 / gcc", "C++11 / g++", "Pascal / fpc", "Java / JDK", + "Python 3 / CPython", "Go"]) + + set_if_present(self.conf, args, 'score_precision') + + logger.info("Loading parameters for contest %s.", args["name"]) + + # If enabled, other token mode settings must be provided through AWS + args['token_mode'] = self.conf.get('token_mode', TOKEN_MODE_DISABLED) + + args['start'] = self.conf.get('start', datetime.datetime(1970, 1, 1)) + args['stop'] = self.conf.get('stop', datetime.datetime(1970, 1, 1)) + + set_if_present(self.conf, args, 'per_user_time', make_timedelta) + + set_if_present(self.conf, args, 'max_submission_number') + set_if_present(self.conf, args, 'max_user_test_number') + set_if_present(self.conf, args, 'min_submission_interval', make_timedelta) + set_if_present(self.conf, args, 'min_user_test_interval', make_timedelta) + + tasks = list(self.conf['tasks'].keys()) + + logger.info("Contest parameters loaded.") + + return Contest(**args), tasks, [] + + def contest_has_changed(self): + # TODO: Detect if the contest has been changed since its last import + return True + + def get_task_loader(self, taskname): + task_yaml_path = os.path.join(taskname, 'task.yaml') + if self.conf['tasks'][taskname] is not None: + task_yaml_path = self.conf['tasks'][taskname].get('config') + task_yaml_path = os.path.join(self.contest_dir, task_yaml_path) + return LioTaskLoader(task_yaml_path, self.file_cacher) + From 08e40b75c54da62e06ad094389123c46d5213aa0 Mon Sep 17 00:00:00 2001 From: PPakalns Date: Thu, 6 Dec 2018 15:04:46 +0200 Subject: [PATCH 5/9] Small bug fixes --- cmscontrib/loaders/lio.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmscontrib/loaders/lio.py b/cmscontrib/loaders/lio.py index db98f865a7..d1f869c2db 100644 --- a/cmscontrib/loaders/lio.py +++ b/cmscontrib/loaders/lio.py @@ -95,7 +95,7 @@ def get_task(self, get_statement): } name = args['name'] - logger.info(f"Loading parameters for task f{name}") + logger.info(f"Loading parameters for task {name}") if get_statement: args['statements'] = {} @@ -199,7 +199,7 @@ def get_task(self, get_statement): return None match = matcher.search(test_filename) if not match: - logger.critical(f"Unsupported file in test archive f{name}") + logger.critical(f"Unsupported file in test archive {name}") return None is_input = match.group(1) == 'i' @@ -308,7 +308,7 @@ def contest_has_changed(self): def get_task_loader(self, taskname): task_yaml_path = os.path.join(taskname, 'task.yaml') if self.conf['tasks'][taskname] is not None: - task_yaml_path = self.conf['tasks'][taskname].get('config') + task_yaml_path = self.conf['tasks'][taskname].get('config', task_yaml_path) task_yaml_path = os.path.join(self.contest_dir, task_yaml_path) return LioTaskLoader(task_yaml_path, self.file_cacher) From dc259731ec6ae65d889d7f2824c0cef2c6caad77 Mon Sep 17 00:00:00 2001 From: PPakalns Date: Thu, 6 Dec 2018 16:39:10 +0200 Subject: [PATCH 6/9] Convert all line endings to '\n' --- cmscontrib/loaders/lio.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmscontrib/loaders/lio.py b/cmscontrib/loaders/lio.py index d1f869c2db..55a9ae4920 100644 --- a/cmscontrib/loaders/lio.py +++ b/cmscontrib/loaders/lio.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import io import os import re import logging @@ -222,14 +223,15 @@ def get_task(self, get_statement): if 'input' not in testcase or 'output' not in testcase: logger.critical(f"Input or output file not found for test {group}{test_in_group}") return None - # TODO: Convert windows newlines to unix newlines (dos2unix) with zip.open(testcase['input'], 'r') as input_file: - input_digest = self.file_cacher.put_file_from_fobj( - input_file, + content = io.TextIOWrapper(input_file, encoding='ascii', newline=None).read() + input_digest = self.file_cacher.put_file_content( + content.encode('ascii'), f"Input {testcase['input']} for task {task.name}") with zip.open(testcase['output'], 'r') as output_file: - output_digest = self.file_cacher.put_file_from_fobj( - output_file, + content = io.TextIOWrapper(output_file, encoding='ascii', newline=None).read() + output_digest = self.file_cacher.put_file_content( + content.encode('ascii'), f"Output {testcase['output']} for task {task.name}") codename = f"{group:03}{test_in_group}" args["testcases"][codename] = \ From 27bc97faba0884ae9ace6577d7b1b0369ad55b11 Mon Sep 17 00:00:00 2001 From: PPakalns Date: Thu, 20 Dec 2018 16:41:41 +0200 Subject: [PATCH 7/9] Added missing default values for Lio loader Default values for following options: timezone, allowed_localizations, minimum interval between submissions, minimum interval between user tests, maximum number of submissions, maximum number of user tests. --- cmscontrib/loaders/lio.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cmscontrib/loaders/lio.py b/cmscontrib/loaders/lio.py index 55a9ae4920..288450e85c 100644 --- a/cmscontrib/loaders/lio.py +++ b/cmscontrib/loaders/lio.py @@ -37,12 +37,14 @@ logger = logging.getLogger(__name__) -def set_if_present(src_dict, trg_dict, key, conv=None): +def set_if_present(src_dict, trg_dict, key, conv=None, default=None): if key in src_dict: if conv: trg_dict[key] = conv(src_dict[key]) else: trg_dict[key] = src_dict[key] + elif default is not None: + trg_dict[key] = default class LioTaskLoader(TaskLoader): @@ -122,8 +124,8 @@ def get_task(self, get_statement): logger.critical("Unknown score mode provided") raise ValueError - set_if_present(self.conf, args, 'max_submission_number') - set_if_present(self.conf, args, 'max_user_test_number') + set_if_present(self.conf, args, 'max_submission_number', default=40) + set_if_present(self.conf, args, 'max_user_test_number', default=40) set_if_present(self.conf, args, 'min_submission_interval', make_timedelta) set_if_present(self.conf, args, 'min_user_test_interval', make_timedelta) @@ -276,6 +278,7 @@ def get_contest(self): 'description': self.conf['description'], } + args['allowed_localizations'] = self.conf.get('allowed_localizations', ['lv']) args['languages'] = self.conf.get('languages', ["C11 / gcc", "C++11 / g++", "Pascal / fpc", "Java / JDK", "Python 3 / CPython", "Go"]) @@ -289,13 +292,16 @@ def get_contest(self): args['start'] = self.conf.get('start', datetime.datetime(1970, 1, 1)) args['stop'] = self.conf.get('stop', datetime.datetime(1970, 1, 1)) + args['timezone'] = self.conf.get('timezone', 'Europe/Riga') set_if_present(self.conf, args, 'per_user_time', make_timedelta) set_if_present(self.conf, args, 'max_submission_number') set_if_present(self.conf, args, 'max_user_test_number') - set_if_present(self.conf, args, 'min_submission_interval', make_timedelta) - set_if_present(self.conf, args, 'min_user_test_interval', make_timedelta) + set_if_present(self.conf, args, 'min_submission_interval', \ + conv=make_timedelta, default=make_timedelta(30)) + set_if_present(self.conf, args, 'min_user_test_interval', \ + conv=make_timedelta, default=make_timedelta(30)) tasks = list(self.conf['tasks'].keys()) From f02f736c05680949aca9dcacfe9f64236fb952b0 Mon Sep 17 00:00:00 2001 From: PPakalns Date: Sun, 1 Dec 2019 10:06:19 +0200 Subject: [PATCH 8/9] Improved error handling, cleaned up --- cmscontrib/loaders/lio.py | 69 ++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/cmscontrib/loaders/lio.py b/cmscontrib/loaders/lio.py index 288450e85c..884bbe05bf 100644 --- a/cmscontrib/loaders/lio.py +++ b/cmscontrib/loaders/lio.py @@ -19,6 +19,7 @@ import io import os import re +import tempfile import logging import yaml import zipfile @@ -37,19 +38,20 @@ logger = logging.getLogger(__name__) -def set_if_present(src_dict, trg_dict, key, conv=None, default=None): +class LioLoaderException(Exception): + pass + + +def set_if_present(src_dict, trg_dict, key, conv=lambda x: x, default=None): if key in src_dict: - if conv: - trg_dict[key] = conv(src_dict[key]) - else: - trg_dict[key] = src_dict[key] + trg_dict[key] = conv(src_dict[key]) elif default is not None: trg_dict[key] = default class LioTaskLoader(TaskLoader): - short_name = "Lio-task" + short_name = "lio-task" description = "Latvian Informatics Olympiad task loader" def __init__(self, path, file_cacher): @@ -64,6 +66,7 @@ def detect(path): return False + # TODO: Read subgroup points from the yaml file when possible def parse_point_file(self, point_path): """ Parse point file with the following format for each line @@ -81,13 +84,13 @@ def parse_point_file(self, point_path): points = int(vars[2]) for group in range(a, b+1): if group in points_per_group: - logger.critical("Duplicated groups in point file") - raise ValueError + raise LioLoaderException("Duplicated groups in point file") points_per_group[group] = points for group in range(len(points_per_group)): if group not in points_per_group: - logger.critical("Missing group from point file") - raise ValueError + raise LioLoaderException("Missing group from point file") + if sum(points_per_group.values()) != 100: + raise LioLoaderException("Points for all groups doesn't sum up to 100") return points_per_group @@ -113,7 +116,7 @@ def get_task(self, get_statement): if args['statements']: args['primary_statements'] = self.conf.get('primary_statements', ["lv"]) - args['submission_format'] = [f"{name}.%l"] + set_if_present(self.conf, args, 'submission_format', default=[f"{name}.%l"]) set_if_present(self.conf, args, 'score_precision') @@ -121,8 +124,7 @@ def get_task(self, get_statement): if score_mode in [SCORE_MODE_MAX, SCORE_MODE_MAX_SUBTASK, SCORE_MODE_MAX_TOKENED_LAST]: args['score_mode'] = score_mode else: - logger.critical("Unknown score mode provided") - raise ValueError + raise LioLoaderException("Unknown score mode provided") set_if_present(self.conf, args, 'max_submission_number', default=40) set_if_present(self.conf, args, 'max_user_test_number', default=40) @@ -153,20 +155,20 @@ def get_task(self, get_statement): if 'checker' in self.conf: logger.info("Checker found, compiling") checker_src = os.path.join(self.task_dir, self.conf['checker']) - checker_exe = os.path.join(os.path.dirname(checker_src), "checker") if config.installed: testlib_path = "/usr/local/include/cms" else: testlib_path = os.path.join(os.path.dirname(__file__), "polygon") - code = subprocess.call(["g++", "-x", "c++", "-O2", "-static", - "-DCMS", "-I", testlib_path, - "-o", checker_exe, checker_src]) - if code != 0: - logger.critical("Could not compile checker") - return None - digest = self.file_cacher.put_file_from_path( - checker_exe, "Checker for task {name}" - ) + with tempfile.TemporaryDirectory() as tmp_dir: + checker_exe = os.path.join(tmp_dir, "checker") + code = subprocess.call(["g++", "-x", "c++", "-O2", "-static", + "-pipe", "-s", "-DCMS", "-I", testlib_path, + "-o", checker_exe, checker_src]) + if code != 0: + raise LioLoaderException("Could not compile checker") + digest = self.file_cacher.put_file_from_path( + checker_exe, "Checker for task {name}" + ) args["managers"]["checker"] = Manager("checker", digest) evaluation_param = "comparator" else: @@ -174,9 +176,6 @@ def get_task(self, get_statement): point_file = os.path.join(self.task_dir, self.conf.get('point_file', 'punkti.txt')) points_per_group = self.parse_point_file(point_file) - if sum(points_per_group.values()) != 100: - logger.critical("Points for all groups doesn't sum up to 100") - return None args["score_type"] = self.conf.get("score_type", "GroupMin") args["score_type_parameters"] = \ @@ -198,12 +197,10 @@ def get_task(self, get_statement): test_files = {} for test_filename in zip.namelist(): if '/' in test_filename or '\\' in test_filename: - logger.critical("Test zip archive contains a directory") - return None + raise LioLoaderException("Test zip archive contains a directory") match = matcher.search(test_filename) if not match: - logger.critical(f"Unsupported file in test archive {name}") - return None + raise LioLoaderException(f"Unsupported file in test archive {name}") is_input = match.group(1) == 'i' group = int(match.group(2)) @@ -218,13 +215,13 @@ def get_task(self, get_statement): testcase['input' if is_input else 'output'] = test_filename # Extract them in correct order and update subtask list and testcases + max_group_digit_length = len(str(max(test_files.keys()))) for group in sorted(test_files.keys()): for test_in_group in sorted(test_files[group].keys()): tests_per_group[group] += 1 testcase = test_files[group][test_in_group] if 'input' not in testcase or 'output' not in testcase: - logger.critical(f"Input or output file not found for test {group}{test_in_group}") - return None + raise LioLoaderException(f"Input or output file not found for test {group}{test_in_group}") with zip.open(testcase['input'], 'r') as input_file: content = io.TextIOWrapper(input_file, encoding='ascii', newline=None).read() input_digest = self.file_cacher.put_file_content( @@ -235,14 +232,13 @@ def get_task(self, get_statement): output_digest = self.file_cacher.put_file_content( content.encode('ascii'), f"Output {testcase['output']} for task {task.name}") - codename = f"{group:03}{test_in_group}" + codename = f"{group:0{max_group_digit_length}}{test_in_group}" args["testcases"][codename] = \ Testcase(codename, group in public_groups, input_digest, output_digest) for i in range(len(tests_per_group)): if tests_per_group[i] == 0: - logger.critical(f"No testcases for group {i}") - return None + raise LioLoaderException(f"No testcases for group {i}") task.active_dataset = Dataset(**args) @@ -259,7 +255,7 @@ def task_has_changed(self): class LioContestLoader(ContestLoader): - short_name = "Lio-contest" + short_name = "lio-contest" description = "Latvian Informatics Olympiad contest loader" def __init__(self, path, file_cacher): @@ -319,4 +315,3 @@ def get_task_loader(self, taskname): task_yaml_path = self.conf['tasks'][taskname].get('config', task_yaml_path) task_yaml_path = os.path.join(self.contest_dir, task_yaml_path) return LioTaskLoader(task_yaml_path, self.file_cacher) - From 9c5fc8b96f300ed34248bc38d5bd2667f9bc385e Mon Sep 17 00:00:00 2001 From: PPakalns Date: Sun, 1 Dec 2019 10:14:25 +0200 Subject: [PATCH 9/9] Fix group identifier in score parameters --- cmscontrib/loaders/lio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmscontrib/loaders/lio.py b/cmscontrib/loaders/lio.py index 884bbe05bf..1e495b9b56 100644 --- a/cmscontrib/loaders/lio.py +++ b/cmscontrib/loaders/lio.py @@ -176,10 +176,11 @@ def get_task(self, get_statement): point_file = os.path.join(self.task_dir, self.conf.get('point_file', 'punkti.txt')) points_per_group = self.parse_point_file(point_file) + max_group_digit_length = len(str(len(points_per_group) - 1)) args["score_type"] = self.conf.get("score_type", "GroupMin") args["score_type_parameters"] = \ - [[points_per_group[i], f"{i:03}"] for i in range(len(points_per_group))] + [[points_per_group[i], f"{i:0{max_group_digit_length}}"] for i in range(len(points_per_group))] args["task_type"] = "Batch" args["task_type_parameters"] = \ [compilation_param, @@ -215,7 +216,6 @@ def get_task(self, get_statement): testcase['input' if is_input else 'output'] = test_filename # Extract them in correct order and update subtask list and testcases - max_group_digit_length = len(str(max(test_files.keys()))) for group in sorted(test_files.keys()): for test_in_group in sorted(test_files[group].keys()): tests_per_group[group] += 1