diff --git a/mo/jobs/submit.py b/mo/jobs/submit.py index 8fe3dfbedee59878a6ed0d2d8f5dd61351587b3f..82d19a8ac380516a0154d1bad93b3805a53d08f6 100644 --- a/mo/jobs/submit.py +++ b/mo/jobs/submit.py @@ -116,9 +116,10 @@ class UploadFeedback: task: Optional[db.Task] = None user: Optional[db.User] = None tmp_name: Optional[str] = None + is_solution: bool = False -def parse_feedback_name(name: str) -> Optional[UploadFeedback]: +def parse_feedback_name(name: str, round_id: int) -> (Optional[UploadFeedback], Optional[str]): name = os.path.basename(name) # Formát jmen generovaný dávkovým stahováním @@ -128,7 +129,7 @@ def parse_feedback_name(name: str) -> Optional[UploadFeedback]: file_name=name, task_code=m['task'], user_id=int(m['user_id']), - ) + ), None # Formát jmen, pod kterými se ukládají jednotlivě stahovaná řešení m = re.match(r'(?P<task>[^_]+)_(reseni|opravene)_(?P<user_id>\d+)_', name) @@ -137,7 +138,7 @@ def parse_feedback_name(name: str) -> Optional[UploadFeedback]: file_name=name, task_code=m['task'], user_id=int(m['user_id']), - ) + ), None # Formát jmen, pod kterými se dříve ukládala jednotlivě stahovaná řešení m = re.match(r'(?P<task>.+)-(reseni|opravene)-(?P<paper_id>\d+)\.', name) @@ -148,9 +149,32 @@ def parse_feedback_name(name: str) -> Optional[UploadFeedback]: file_name=name, task_code=m['task'], user_id=paper.for_user, - ) + ), None + + # Hromadný upload skenů + m = re.match(r'(?P<task>[^_]+)_(?P<what>reseni|opravene)_(?P<lastname>[^_]+)_(?P<firstname>[^_]+)\.', name) + if m: + users = (db.get_session().query(db.User.user_id) + .select_from(db.Participation) + .join(db.User, db.User.user_id == db.Participation.user_id) + .join(db.Contest, db.Contest.master_contest_id == db.Participation.contest_id) + .filter(db.Contest.round_id == round_id) + .filter(and_( + db.f_unaccent(db.User.first_name).ilike(db.f_unaccent(m['firstname'])), + db.f_unaccent(db.User.last_name ).ilike(db.f_unaccent(m['lastname'])))) + .all()) + if len(users) > 1: + return None, f'Nalezeno více soutěžící se jménem "{m["firstname"]} {m["lastname"]}"' + elif len(users) == 0: + return None, f'Nenalezen žádný soutěžících se jménem "{m["firstname"]} {m["lastname"]}".' + return UploadFeedback( + file_name=name, + task_code=m['task'], + user_id=users[0].user_id, + is_solution=(m['what']=='reseni'), + ), None - return None + return None, f'Nerozpoznáno jméno souboru {name}.' @job_handler(db.JobType.upload_feedback) @@ -196,7 +220,7 @@ def handle_upload_feedback(the_job: TheJob): contents = zip.infolist() for item in contents: if not item.is_dir(): - fb = parse_feedback_name(item.filename) + fb, err = parse_feedback_name(item.filename, round_id) if fb: tmp_file = NamedTemporaryFile(mode='w+b', delete=False) logger.debug(f'Job: Extrahuji {item.filename} do {tmp_file.name}') @@ -206,7 +230,7 @@ def handle_upload_feedback(the_job: TheJob): fb.tmp_name = tmp_file.name files.append(fb) else: - the_job.error(f'Nerozpoznáno jméno souboru {item.filename}') + the_job.error(err) except zipfile.BadZipFile as e: logger.debug(f'Job: Nahraný soubor není validní ZIP: {str(e)}') the_job.error('Chybný formát souboru. Je to opravdu ZIP?') @@ -266,7 +290,7 @@ def handle_upload_feedback(the_job: TheJob): paper = db.Paper( for_user_obj=fb.user, task=fb.task, - type=db.PaperType.feedback, + type=db.PaperType.solution if fb.is_solution else db.PaperType.feedback, uploaded_by_obj=job.user, ) try: @@ -279,7 +303,10 @@ def handle_upload_feedback(the_job: TheJob): .filter_by(task=fb.task, user=fb.user) .with_for_update() .one()) - sol.final_feedback_obj = paper + if fb.is_solution: + sol.final_submit_obj = paper + else: + sol.final_feedback_obj = paper sess.commit() return True diff --git a/mo/web/templates/org_generic_batch_upload.html b/mo/web/templates/org_generic_batch_upload.html index 82fbf74a531a8141f9f4b3dc8d4879e0376981da..e31b913066330416a9d190e3fb270ad5ed810100 100644 --- a/mo/web/templates/org_generic_batch_upload.html +++ b/mo/web/templates/org_generic_batch_upload.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} -{% block title %}Nahrání opravených řešení{% if task %} úlohy {{ task.code }}: {{ task.name }}{% endif %}{% endblock %} +{% block title %}Nahrání odevzdaných či opravených řešení{% if task %} úlohy {{ task.code }}: {{ task.name }}{% endif %}{% endblock %} {% block breadcrumbs %} {{ ctx.breadcrumbs(action="Nahrát ZIP řešení") }} {% endblock %} @@ -9,12 +9,11 @@ {% block body %} <p>Zde můžete nahrát najednou více PDF souborů zabalených do jednoho souboru typu ZIP. -<p>Soubory opravených řešení se musí jmenovat stejně jako původní soubory účastnických řešení. -Na zařazení souborů do adresářů nezáleží. - <p>Pokud ZIP obsahuje jen podmnožinu účastníků, řešení ostatních účastníků se nezmění. Pokud nahrajete řešení téhož účastníka znovu, uloží se nová verze. +<p>Na zařazení souborů do adresářů <i>nezáleží</i>. + <form action="https://{{ bucket_name }}.storage.googleapis.com" method="post" class="form" enctype="multipart/form-data" role="form"> {% for key, value in policy.items() %} <input type="hidden" name="{{ key }}" value="{{ value }}"> @@ -25,4 +24,21 @@ Pokud nahrajete řešení téhož účastníka znovu, uloží se nová verze. <input class="btn btn-primary" type="submit" value="Odeslat"> </form> +<h4>Opravená řešení</h4> + +<p>Soubory opravených řešení se musí jmenovat stejně jako původně z Osmo stažené soubory odevzdaných řešení. +<p>Alternativně použijte formát: +<pre> +<<i>kód úlohy</i>>_opravene_<<i>příjmení</i>>_<<i>jméno</i>>.pdf +</pre> +<p>tj. například <kbd>B2-1_opravene_pilny_josef.pdf</kbd>. U jména a příjmení nezáleží na diakritice, ani velikosti písmen. Pokud existuje v rámci kola více soutěžících se stejným jménem, skončí nahrání chybou, jejich řešení je třeba nahrát jednotlivě. + +<h4>Odevzdaná řešení</h4> + +<p>Soubory odevzdaných řešení musí být pojmenované podle této šablony: +<pre> +<<i>kód úlohy</i>>_reseni_<<i>příjmení</i>>_<<i>jméno</i>>.pdf +</pre> +<p>tj. například <kbd>B2-1_reseni_pilny_josef.pdf</kbd>. + {% endblock %}