This commit is contained in:
+57
-20
@@ -37,6 +37,32 @@ def _users_by_id(user_ids):
|
|||||||
return {user.id: user for user in User.query.filter(User.id.in_(wanted)).all()}
|
return {user.id: user for user in User.query.filter(User.id.in_(wanted)).all()}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_score(score_value):
|
||||||
|
"""Turn a raw form value into a criterion score (1..10), or None.
|
||||||
|
|
||||||
|
A blank value means "not assessed". Anything else outside 1..10 is a
|
||||||
|
validation error, matching the single-player form's ``EvaluationSchema``
|
||||||
|
-- a coach must not be able to quietly turn 11 or 0 into "no score"
|
||||||
|
(QUA-003).
|
||||||
|
"""
|
||||||
|
if score_value is None or str(score_value).strip() == '':
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
score = int(score_value)
|
||||||
|
except (TypeError, ValueError) as exc:
|
||||||
|
raise ValidationError('A score must be a whole number from 1 to 10.') from exc
|
||||||
|
if not 1 <= score <= 10:
|
||||||
|
raise ValidationError('Scores run from 1 to 10.')
|
||||||
|
return score
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_evaluation(evaluation, scores, comments, position):
|
||||||
|
"""Write validated scores, comments and position onto an Evaluation."""
|
||||||
|
evaluation.apply_scores(scores)
|
||||||
|
evaluation.comments = comments
|
||||||
|
evaluation.position_recommendation = position
|
||||||
|
|
||||||
|
|
||||||
@evaluations_bp.route('')
|
@evaluations_bp.route('')
|
||||||
@login_required
|
@login_required
|
||||||
def list_evaluations():
|
def list_evaluations():
|
||||||
@@ -317,27 +343,38 @@ def batch_evaluate(tryout_id):
|
|||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
saved = 0
|
saved = 0
|
||||||
for entry in players:
|
try:
|
||||||
pid = entry['player'].id
|
for entry in players:
|
||||||
scores = {
|
pid = entry['player'].id
|
||||||
field_name: validate_score(request.form.get(f'{field_name}_{pid}'))
|
scores = {
|
||||||
for field_name, _ in EVALUATION_CRITERIA
|
field_name: validate_score(request.form.get(f'{field_name}_{pid}'))
|
||||||
}
|
for field_name, _ in EVALUATION_CRITERIA
|
||||||
comments = request.form.get(f'comments_{pid}')
|
}
|
||||||
position = request.form.get(f'position_recommendation_{pid}')
|
comments = request.form.get(f'comments_{pid}')
|
||||||
|
position = request.form.get(f'position_recommendation_{pid}')
|
||||||
|
|
||||||
existing = entry['existing']
|
existing = entry['existing']
|
||||||
if existing:
|
if existing:
|
||||||
_apply_evaluation(existing, scores, comments, position)
|
_apply_evaluation(existing, scores, comments, position)
|
||||||
else:
|
else:
|
||||||
evaluation = Evaluation(
|
evaluation = Evaluation(
|
||||||
tryout_id=tryout_id,
|
tryout_id=tryout_id,
|
||||||
player_id=pid,
|
player_id=pid,
|
||||||
evaluator_id=current_user.id,
|
evaluator_id=current_user.id,
|
||||||
)
|
)
|
||||||
_apply_evaluation(evaluation, scores, comments, position)
|
_apply_evaluation(evaluation, scores, comments, position)
|
||||||
db.session.add(evaluation)
|
db.session.add(evaluation)
|
||||||
saved += 1
|
saved += 1
|
||||||
|
except ValidationError:
|
||||||
|
db.session.rollback()
|
||||||
|
flash(_('Scores must be whole numbers from 1 to 10.'), 'danger')
|
||||||
|
return render_template(
|
||||||
|
'pages/batch_evaluate.html',
|
||||||
|
tryout=tryout,
|
||||||
|
players=players,
|
||||||
|
evaluation_criteria=EVALUATION_CRITERIA,
|
||||||
|
game_positions=GAME_POSITIONS,
|
||||||
|
)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(f'Saved evaluations for {saved} player(s).', 'success')
|
flash(f'Saved evaluations for {saved} player(s).', 'success')
|
||||||
|
|||||||
@@ -179,3 +179,61 @@ class TestThroughTheForm:
|
|||||||
evaluation = Evaluation.query.one()
|
evaluation = Evaluation.query.one()
|
||||||
assert evaluation.mecanics_score is None
|
assert evaluation.mecanics_score is None
|
||||||
assert evaluation.overall_score == 4
|
assert evaluation.overall_score == 4
|
||||||
|
|
||||||
|
|
||||||
|
class TestBatchEvaluate:
|
||||||
|
"""The batch page drives the same two helpers the single-player form does,
|
||||||
|
through ``{field}_{player_id}`` names. The helpers were lost once, in a
|
||||||
|
refactor, and the page 500'd on every POST until someone noticed."""
|
||||||
|
|
||||||
|
def _submit(self, client, setup, **fields):
|
||||||
|
data = {'player_ids': [str(setup['player_id'])]}
|
||||||
|
data.update(fields)
|
||||||
|
return client.post(
|
||||||
|
f'/evaluations/{setup["tryout_id"]}/batch',
|
||||||
|
data=data,
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_a_valid_batch_evaluation_is_stored(self, app, client, evaluation_setup):
|
||||||
|
pid = evaluation_setup['player_id']
|
||||||
|
self._submit(
|
||||||
|
client,
|
||||||
|
evaluation_setup,
|
||||||
|
**{
|
||||||
|
f'mecanics_score_{pid}': '8',
|
||||||
|
f'mental_score_{pid}': '6',
|
||||||
|
f'comments_{pid}': 'Solid.',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
evaluation = Evaluation.query.one()
|
||||||
|
assert evaluation.mecanics_score == 8
|
||||||
|
assert evaluation.overall_score == 7
|
||||||
|
assert evaluation.comments == 'Solid.'
|
||||||
|
assert evaluation.position_recommendation is None
|
||||||
|
|
||||||
|
def test_an_out_of_range_batch_score_stores_nothing(self, app, client, evaluation_setup):
|
||||||
|
pid = evaluation_setup['player_id']
|
||||||
|
self._submit(
|
||||||
|
client,
|
||||||
|
evaluation_setup,
|
||||||
|
**{f'mecanics_score_{pid}': '11', f'mental_score_{pid}': '6'},
|
||||||
|
)
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
assert Evaluation.query.count() == 0
|
||||||
|
|
||||||
|
def test_a_second_batch_submission_replaces_the_first(self, app, client, evaluation_setup):
|
||||||
|
pid = evaluation_setup['player_id']
|
||||||
|
self._submit(
|
||||||
|
client, evaluation_setup, **{f'mecanics_score_{pid}': '8', f'mental_score_{pid}': '4'}
|
||||||
|
)
|
||||||
|
self._submit(
|
||||||
|
client, evaluation_setup, **{f'mecanics_score_{pid}': '4', f'mental_score_{pid}': '4'}
|
||||||
|
)
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
evaluation = Evaluation.query.one()
|
||||||
|
assert evaluation.overall_score == 4
|
||||||
|
|||||||
Reference in New Issue
Block a user