Skip to content
Snippets Groups Projects
Commit ed29dd9c authored by Jan Prachař's avatar Jan Prachař
Browse files

O něco blbuvzdornější import účastníků

* Varování, pokud je pravděpodobně překlep v názvu sloupce a při importu
  pak dojde k chybě.
* Rozlišení, jestli je fomát ročníku uplně špatně, nebo jen neodpovídá
  typu školy.
parent c52c69b9
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !39. Comments created here will be created in the context of that merge request.
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
# - quoting pomocí uvozovek # - quoting pomocí uvozovek
import csv import csv
import difflib
from enum import auto from enum import auto
from dataclasses import dataclass, fields from dataclasses import dataclass, fields
from typing import Type, List, IO, Sequence from typing import Type, List, IO, Sequence
...@@ -108,6 +109,7 @@ def write(file: IO, fmt: FileFormat, row_class: Type[Row], rows: Sequence[Row]): ...@@ -108,6 +109,7 @@ def write(file: IO, fmt: FileFormat, row_class: Type[Row], rows: Sequence[Row]):
def read(file: IO, fmt: FileFormat, row_class: Type[Row]): def read(file: IO, fmt: FileFormat, row_class: Type[Row]):
reader = csv.reader(file, dialect=fmt.get_dialect(), strict=True) reader = csv.reader(file, dialect=fmt.get_dialect(), strict=True)
warnings = []
header: List[str] = [] header: List[str] = []
rows: List[Row] = [] rows: List[Row] = []
columns = set(field.name for field in fields(row_class)) columns = set(field.name for field in fields(row_class))
...@@ -120,6 +122,13 @@ def read(file: IO, fmt: FileFormat, row_class: Type[Row]): ...@@ -120,6 +122,13 @@ def read(file: IO, fmt: FileFormat, row_class: Type[Row]):
header = r header = r
if not any(h in columns for h in header): if not any(h in columns for h in header):
raise MissingHeaderError() raise MissingHeaderError()
for h in header:
if not h in columns:
best_matches = difflib.get_close_matches(h, columns, n=1, cutoff=0.8)
if best_matches:
warnings.append(
"Neznámý sloupec '{}', měli jste na mysli '{}'?".format(
h, best_matches[0]))
else: else:
row = row_class() row = row_class()
not_empty = False not_empty = False
...@@ -133,4 +142,4 @@ def read(file: IO, fmt: FileFormat, row_class: Type[Row]): ...@@ -133,4 +142,4 @@ def read(file: IO, fmt: FileFormat, row_class: Type[Row]):
if not_empty: if not_empty:
rows.append(row) rows.append(row)
return rows return (rows, warnings)
...@@ -69,6 +69,7 @@ class Import: ...@@ -69,6 +69,7 @@ class Import:
def __init__(self): def __init__(self):
self.errors = [] self.errors = []
self.warnings = []
  • Pardon, ještě maličkost: atributu warnings bys měl o kousek výše deklarovat typ (podobně jako u errors).

  • Author Developer

    Ajo, jasný! Hotovo

  • Please register or sign in to reply
self.rr = None self.rr = None
self.place_cache = {} self.place_cache = {}
self.school_place_cache = {} self.school_place_cache = {}
...@@ -164,11 +165,16 @@ class Import: ...@@ -164,11 +165,16 @@ class Import:
# lidé připisují všechny možné i nemožné znaky, které vypadají jako apostrof :) # lidé připisují všechny možné i nemožné znaky, které vypadají jako apostrof :)
rocnik = re.sub('[\'"\u00b4\u2019]', "", rocnik) rocnik = re.sub('[\'"\u00b4\u2019]', "", rocnik)
if (school.is_ss and re.fullmatch(r'\d/\d', rocnik) if (not re.fullmatch(r'\d(/\d)?', rocnik)):
or school.is_zs and re.fullmatch(r'\d', rocnik)): return self.error(f'Ročník má neplatný formát, musí to být buď číslice, nebo číslice/číslice')
return rocnik
if (not school.is_zs and re.fullmatch(r'\d', rocnik)):
return self.error(f'Ročník pro střední školu ({school.place.name}) zapisujte ve formátu číslice/číslice')
return self.error('Ročník neodpovídá typu školy: pro základní je to číslice, pro střední číslice/číslice') if (not school.is_ss and re.fullmatch(r'\d/\d', rocnik)):
return self.error(f'Ročník pro základní školu ({school.place.name}) zapisujte jako číslici 1–9')
return rocnik
def parse_born(self, rok: str) -> Optional[int]: def parse_born(self, rok: str) -> Optional[int]:
if not re.fullmatch(r'\d{4}', rok): if not re.fullmatch(r'\d{4}', rok):
...@@ -377,9 +383,11 @@ class Import: ...@@ -377,9 +383,11 @@ class Import:
try: try:
with open(path, encoding=charset) as file: with open(path, encoding=charset) as file:
try: try:
rows: List[mo.csv.Row] = mo.csv.read(file=file, fmt=self.fmt, row_class=self.row_class) rows: List[mo.csv.Row]
rows, warnings = mo.csv.read(file=file, fmt=self.fmt, row_class=self.row_class)
self.warnings += warnings
except MissingHeaderError: except MissingHeaderError:
return self.error('Souboru chybí hlavička s názvy sloupců') return self.error('Souboru chybí první řádek s názvy sloupců')
except UnicodeDecodeError: except UnicodeDecodeError:
return self.error(f'Soubor není v kódování {self.fmt.get_charset()}') return self.error(f'Soubor není v kódování {self.fmt.get_charset()}')
except Exception as e: except Exception as e:
... ...
......
...@@ -364,6 +364,7 @@ def generic_import(round: db.Round, contest: Optional[db.Contest]): ...@@ -364,6 +364,7 @@ def generic_import(round: db.Round, contest: Optional[db.Contest]):
form = ImportForm() form = ImportForm()
errs = [] errs = []
warnings = []
if form.validate_on_submit(): if form.validate_on_submit():
fmt = form.fmt.data fmt = form.fmt.data
imp = create_import(user=g.user, type=form.typ.data, fmt=fmt, round=round, contest=contest) imp = create_import(user=g.user, type=form.typ.data, fmt=fmt, round=round, contest=contest)
...@@ -383,6 +384,7 @@ def generic_import(round: db.Round, contest: Optional[db.Contest]): ...@@ -383,6 +384,7 @@ def generic_import(round: db.Round, contest: Optional[db.Contest]):
return redirect(url_for('org_round', id=round.round_id)) return redirect(url_for('org_round', id=round.round_id))
else: else:
errs = imp.errors errs = imp.errors
warnings = imp.warnings
else: else:
flash('Vyberte si prosím soubor', 'danger') flash('Vyberte si prosím soubor', 'danger')
elif form.get_template.data: elif form.get_template.data:
...@@ -398,6 +400,7 @@ def generic_import(round: db.Round, contest: Optional[db.Contest]): ...@@ -398,6 +400,7 @@ def generic_import(round: db.Round, contest: Optional[db.Contest]):
round=round, round=round,
form=form, form=form,
errs=errs, errs=errs,
warnings=warnings
) )
...@@ -1283,6 +1286,7 @@ def generic_batch_points(round: db.Round, contest: Optional[db.Contest], task: d ...@@ -1283,6 +1286,7 @@ def generic_batch_points(round: db.Round, contest: Optional[db.Contest], task: d
form = BatchPointsForm() form = BatchPointsForm()
errs = [] errs = []
warnings = []
if form.validate_on_submit(): if form.validate_on_submit():
fmt = form.fmt.data fmt = form.fmt.data
imp = create_import(user=g.user, type=ImportType.points, fmt=fmt, round=round, contest=contest, task=task, allow_add_del=form.add_del_sols.data) imp = create_import(user=g.user, type=ImportType.points, fmt=fmt, round=round, contest=contest, task=task, allow_add_del=form.add_del_sols.data)
...@@ -1302,6 +1306,7 @@ def generic_batch_points(round: db.Round, contest: Optional[db.Contest], task: d ...@@ -1302,6 +1306,7 @@ def generic_batch_points(round: db.Round, contest: Optional[db.Contest], task: d
return redirect(url_for('org_round', id=round.round_id)) return redirect(url_for('org_round', id=round.round_id))
else: else:
errs = imp.errors errs = imp.errors
warnings = imp.warnings
else: else:
flash('Vyberte si prosím soubor', 'danger') flash('Vyberte si prosím soubor', 'danger')
elif form.get_template.data: elif form.get_template.data:
...@@ -1316,6 +1321,7 @@ def generic_batch_points(round: db.Round, contest: Optional[db.Contest], task: d ...@@ -1316,6 +1321,7 @@ def generic_batch_points(round: db.Round, contest: Optional[db.Contest], task: d
round=round, contest=contest, task=task, round=round, contest=contest, task=task,
form=form, form=form,
errs=errs, errs=errs,
warnings=warnings
) )
... ...
......
...@@ -8,6 +8,16 @@ ...@@ -8,6 +8,16 @@
{% block body %} {% block body %}
{% if warnings %}
<h3>Varování při importu</h3>
<div class="alert alert-warning" role="alert" style="white-space: pre-line">{{ "" -}}
{% for e in warnings %}
{{ e }}
{% endfor %}
</div>
{% endif %}
{% if errs %} {% if errs %}
<h3>Chyby při importu</h3> <h3>Chyby při importu</h3>
... ...
......
...@@ -9,14 +9,24 @@ Import dat do {% if contest %}soutěžní oblasti {{ contest.place.name }}{% els ...@@ -9,14 +9,24 @@ Import dat do {% if contest %}soutěžní oblasti {{ contest.place.name }}{% els
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{% if warnings %}
<h3>Varování při importu</h3>
<div class="alert alert-warning" role="alert" style="white-space: pre-line">{{ "" -}}
{% for e in warnings %}
{{ e }}
{% endfor %}
</div>
{% endif %}
{% if errs %} {% if errs %}
<h3>Chyby při importu</h3> <h3>Chyby při importu</h3>
<pre><div class="alert alert-danger" role="alert">{{ "" -}} <div class="alert alert-danger" role="alert" style="white-space: pre-line">{{ "" -}}
{% for e in errs %} {% for e in errs %}
{{ e }} {{ e }}
{% endfor %} {% endfor %}
</div></pre> </div>
{% endif %} {% endif %}
<p>Zde je možné importovat účastníky soutěže, dozor na soutěžních místech a opravovatele. <p>Zde je možné importovat účastníky soutěže, dozor na soutěžních místech a opravovatele.
... ...
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment