feat(review): inline file uploader instead of redirect home
When a user lands on Review without an upload, show a file uploader on the page itself and auto-run the analyzer once a file is picked, rather than bouncing them to the home page with a "Back to home" button. Auto-analyze is the right default here: the user is already on the Review page, so they've implicitly committed to a scan. Stashing the bytes in the same session-state keys the home page uses keeps the rest of the flow (encoding picker, gate, tool pages) unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -286,15 +286,51 @@ st.caption(
|
||||
"the rest before applying."
|
||||
)
|
||||
|
||||
# Pre-flight: nothing to review without an upload.
|
||||
# Pre-flight: if nothing has been uploaded yet, let the user upload
|
||||
# directly from this page instead of bouncing them back to the home
|
||||
# screen. Once a file is picked, we auto-run the analyzer (the user is
|
||||
# already on the Review page — they've implicitly committed to a scan),
|
||||
# stash the result, and rerun so the rest of the page picks it up.
|
||||
findings: list[Finding] = st.session_state.get("home_findings") or []
|
||||
upload_name = st.session_state.get("home_uploaded_name")
|
||||
|
||||
if not upload_name:
|
||||
st.warning("No file uploaded. Go back to the home page and upload a CSV or Excel file first.")
|
||||
if st.button("Back to home"):
|
||||
st.switch_page("app.py")
|
||||
st.stop()
|
||||
st.info(
|
||||
"Upload a CSV or Excel file to begin reviewing. The analyzer runs "
|
||||
"locally and your data never leaves this computer."
|
||||
)
|
||||
review_upload = st.file_uploader(
|
||||
"Choose a file",
|
||||
type=["csv", "tsv", "xlsx", "xls"],
|
||||
key="review_upload",
|
||||
help="Drag-and-drop or browse for a CSV, TSV, or Excel file.",
|
||||
)
|
||||
if review_upload is None:
|
||||
st.stop()
|
||||
|
||||
# New file → stash bytes + size + name, drop any stale state, then
|
||||
# run the analyzer. The rerun at the bottom lets the rest of this
|
||||
# page render with the upload in place.
|
||||
same_file = (
|
||||
st.session_state.get("home_uploaded_name") == review_upload.name
|
||||
and st.session_state.get("home_uploaded_size") == review_upload.size
|
||||
)
|
||||
if not same_file:
|
||||
st.session_state["home_uploaded_name"] = review_upload.name
|
||||
st.session_state["home_uploaded_size"] = review_upload.size
|
||||
st.session_state["home_uploaded_bytes"] = review_upload.getvalue()
|
||||
st.session_state.pop("home_findings", None)
|
||||
st.session_state.pop("home_skipped", None)
|
||||
st.session_state.pop("review_decisions", None)
|
||||
st.session_state.pop("normalization_result", None)
|
||||
st.session_state.pop("normalization_for", None)
|
||||
st.session_state.pop("encoding_override", None)
|
||||
|
||||
if st.session_state.get("home_findings") is None:
|
||||
with st.spinner("Analyzing…"):
|
||||
st.session_state["home_findings"] = _run_analysis_with_override(None)
|
||||
st.session_state["home_skipped"] = False
|
||||
st.rerun()
|
||||
|
||||
# ---- Encoding picker --------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -156,16 +156,24 @@ class TestPipelineRunnerWorkflow:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestReviewWorkflow:
|
||||
"""The Review page is the gate-fixer. Without an upload it shows a
|
||||
'go back to home' message. With an upload it runs the analyzer and
|
||||
shows findings."""
|
||||
"""The Review page is the gate-fixer. Without an upload it shows
|
||||
its own file uploader so the user can start the flow from this
|
||||
page directly. With an upload it runs the analyzer and shows
|
||||
findings."""
|
||||
|
||||
def test_no_upload_shows_back_to_home(self, app_factory):
|
||||
def test_no_upload_shows_inline_uploader(self, app_factory):
|
||||
app = app_factory("0_Review")
|
||||
app.run()
|
||||
text = collected_text(app)
|
||||
# Page shows ``No file uploaded`` + ``Back to home``.
|
||||
assert "No file uploaded" in text or "uploaded" in text.lower()
|
||||
# Page should invite the user to upload, not redirect home.
|
||||
assert "Upload" in text or "Choose a file" in text, (
|
||||
f"Review page should expose an inline uploader; got:\n{text[:400]}"
|
||||
)
|
||||
# The 'Back to home' button is gone — the page is self-contained now.
|
||||
labels = [b.label for b in app.button]
|
||||
assert not any("Back to home" in lbl for lbl in labels), (
|
||||
f"Back-to-home button should be removed; got buttons: {labels}"
|
||||
)
|
||||
|
||||
def test_with_upload_shows_review_content(
|
||||
self, app_factory, small_csv_bytes,
|
||||
|
||||
Reference in New Issue
Block a user