fix(analysis): the review findings - rising gravity, undated readings, the band
A second, deeper review of PR #12 found five things the first review did not. All are fixed here. The band was not the band ------------------------- The fermentation curve's shaded range was drawn as ONE dataset with `fill: "+1"`, which fills to the NEXT dataset - and the next dataset was the median line, not the maximum. So the band shaded min-to-median, the upper half of the range was drawn as a hairline but never shaded, and the tooltip said "range min to max" while the picture showed something else. The band is now two datasets - the min curve filled down to a max curve - with the max hidden from the legend. Rising gravity -------------- `attenuation_percent` rejected a final gravity below 1.000 and one implying more than 100% attenuation, but a run whose gravity went UP produced a NEGATIVE attenuation - which was then averaged into the headline figure and plotted as a negative bar. Verified against the live code: OG 1010 -> FG 1020 gave -100.0%. Now rejected, and reported by `implausible_runs` with the reason "the gravity went up, which is usually a mis-ordered pair of readings". Undated readings ---------------- An undated reading sorted to the front of the run, so it became the original gravity - which put the OG after the FG and produced a negative attenuation. The OG now comes from the earliest *dated* reading, and `days_to_target` skips the readings it cannot place on the timeline rather than giving up on the whole run. Verified: readings 1050 (dated), 1005 (undated), 1010 (dated) gave None instead of 24 days, and an OG of 1005 instead of 1050. Active runs on the curve ------------------------ The curve chart filtered on `curve.length > 1` only, so it plotted the active runs as well - a partial fermentation on a chart meant to describe how a completed one behaves. It now excludes them, along with the suspect runs. Four fetches, one payload ------------------------- `/api/runs` carries every reading of every run, and four charts each fetched it independently - so the analysis page made two identical round-trips of the whole collection. There is now one shared fetch. Also ---- - The "Readings that need a second look" table links to the run, so a suspect reading can be corrected in one click. - `days_to_1015` and the `curves` variable were computed and never used. Removed. - A reading below 1.000 now says what it is - "beer cannot be less dense than water, so this is almost certainly a typo (a 1.012 reading entered as 012?)". The reading is still SAVED: rejecting it would lose the brewer's actual observation, and seeing it recorded is how they spot the mistake. 142 tests (up from 134).
This commit is contained in:
@@ -49,6 +49,10 @@ class ReadingForm(FlaskForm):
|
||||
beer_run_type = StringField("Beer / cider name",
|
||||
validators=[Optional(), Length(max=120)])
|
||||
Date = DateField("Reading date", validators=[DataRequired()])
|
||||
# The floor is deliberately lower than is_plausible_sg's. An
|
||||
# out-of-range reading is SAVED with a warning rather than rejected:
|
||||
# the brewer may have mis-typed it, and seeing it recorded is how they
|
||||
# spot and correct the mistake.
|
||||
sg = IntegerField("Gravity reading (points)",
|
||||
validators=[DataRequired(), NumberRange(min=900, max=1500)])
|
||||
final_reading = BooleanField("This is the final reading")
|
||||
@@ -82,11 +86,24 @@ def index():
|
||||
if form.validate_on_submit():
|
||||
sg_points = int(form.sg.data)
|
||||
if not is_plausible_sg(sg_points):
|
||||
flash(
|
||||
f"{sg_points} is outside the usual hydrometer range "
|
||||
"(990-1200). It was saved anyway — double-check the reading.",
|
||||
"warning",
|
||||
)
|
||||
if sg_points < 1000:
|
||||
# Beer cannot be less dense than water, so this is always
|
||||
# a mis-entry - and a mis-typed "1.012" as "012" is the
|
||||
# likely cause. It is still saved so the mistake is
|
||||
# visible and can be corrected.
|
||||
flash(
|
||||
f"{sg_points} is below 1.000 — beer cannot be less dense "
|
||||
"than water, so this is almost certainly a typo (a 1.012 "
|
||||
"reading entered as 012?). It was saved anyway; correct "
|
||||
"it on the run's page.",
|
||||
"warning",
|
||||
)
|
||||
else:
|
||||
flash(
|
||||
f"{sg_points} is outside the usual hydrometer range "
|
||||
"(990-1200). It was saved anyway — double-check the reading.",
|
||||
"warning",
|
||||
)
|
||||
repo().upsert_reading(
|
||||
beer_run_id=str(form.beer_run_id.data),
|
||||
beer_type=(form.beer_run_type.data or "").strip(),
|
||||
|
||||
Reference in New Issue
Block a user