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] 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" diff --git a/cms/server/static/favicon.ico b/cms/server/static/favicon.ico index 66d3497a05..4d65406496 100644 Binary files a/cms/server/static/favicon.ico and b/cms/server/static/favicon.ico differ 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..1e495b9b56 --- /dev/null +++ b/cmscontrib/loaders/lio.py @@ -0,0 +1,317 @@ +#!/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 io +import os +import re +import tempfile +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__) + + +class LioLoaderException(Exception): + pass + + +def set_if_present(src_dict, trg_dict, key, conv=lambda x: x, default=None): + if key in src_dict: + trg_dict[key] = conv(src_dict[key]) + elif default is not None: + trg_dict[key] = default + + +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 + + + # 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 + {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: + 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: + 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 + + + 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 {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"]) + + set_if_present(self.conf, args, 'submission_format', default=[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: + 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) + 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']) + if config.installed: + testlib_path = "/usr/local/include/cms" + else: + testlib_path = os.path.join(os.path.dirname(__file__), "polygon") + 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: + 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) + 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:0{max_group_digit_length}}"] 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: + raise LioLoaderException("Test zip archive contains a directory") + match = matcher.search(test_filename) + if not match: + raise LioLoaderException(f"Unsupported file in test archive {name}") + + 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: + 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( + content.encode('ascii'), + f"Input {testcase['input']} for task {task.name}") + with zip.open(testcase['output'], 'r') as 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: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: + raise LioLoaderException(f"No testcases for group {i}") + + 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['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"]) + + 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)) + 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', \ + 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()) + + 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) + task_yaml_path = os.path.join(self.contest_dir, task_yaml_path) + return LioTaskLoader(task_yaml_path, self.file_cacher)