Skip to content

refactor(corrections): replace action string constants with an Action StrEnum - #312

Open
iabaako wants to merge 1 commit into
feat/296-corrections-foundationfrom
refactor/310-action-strenum
Open

iabaako wants to merge 1 commit into
feat/296-corrections-foundationfrom
refactor/310-action-strenum

Conversation

@iabaako

@iabaako iabaako commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Pull Request Summary 🚀

Stacked on #307 (feat/296-corrections-foundation). Review that PR first. Part of #295. Closes #310.

What does this PR do? 📝

Replaces the correction action string constants with a single Action StrEnum in correction_log.py. Behaviour does not change.

 # src/datasure/processing/correction_log.py
-MODIFY_VALUE_ACTION = "modify value"
-REMOVE_VALUE_ACTION = "remove value"
-REMOVE_ROW_ACTION = "remove row"
-CORRECTION_ACTIONS = (MODIFY_VALUE_ACTION, REMOVE_VALUE_ACTION, REMOVE_ROW_ACTION)
-ACCEPT_ACTION = "accept"
+class Action(StrEnum):
+    MODIFY_VALUE = "modify value"
+    REMOVE_VALUE = "remove value"
+    REMOVE_ROW = "remove row"
+    ACCEPT = "accept"
+
+CORRECTION_ACTIONS: tuple[Action, ...] = (
+    Action.MODIFY_VALUE, Action.REMOVE_VALUE, Action.REMOVE_ROW,
+)
src/datasure/
├── processing/correction_log.py     # owns Action + CORRECTION_ACTIONS
├── processing/corrections.py        # CorrectionEntry.action, signatures -> Action
├── utils/correction_form.py         # CorrectionFormState.action, actions: Sequence[Action]
├── views/correction_view.py         # _handle_apply_correction(action: Action)
└── replication/                     # script_generators.py, package_builder.py use Action members

Merge danger. This is a two-way door: no stored value changes and no migration is needed, so a revert is clean. Blast radius: type-level. Only the *_ACTION names change, and they exist only on the unreleased #307 branch.

Why is this change needed? 🤔

The action vocabulary was plain str. A typo or an unknown action was only caught at runtime, if at all. The check-page sub-issues of #295 (#302, #303, #304) are about to add actions, and each one can now be added as an enum member.

How was this implemented? 🛠️

  • Member values equal the strings already stored in corr_log_{alias} tables. StrEnum members compare equal to those strings and str() returns them. So existing logs load, filter (pl.col("action") == Action.ACCEPT), display and replay unchanged.
  • _apply_entry checks entry.action not in CORRECTION_ACTIONS instead of an inline tuple.
  • _emit_stata, _describe_correction_row and the replay loop receive raw strings from saved logs, so they still take str. Converting with Action(...) would raise on unknown values that are skipped today.
  • The optional match statement or shared mapping (scope item 4) is not done. It would grow the diff, and Corrections: replace action string constants with an Action StrEnum in correction_log.py #310 says to leave it out in that case. The remaining duplication is between _apply_action and the replay loop in corrections.py.
  • docs/ARCHITECTURE.md now notes that correction_log.py owns the Action enum.

How to test or reproduce ? 🧪

  • just test: 3348 passed, 6 skipped.
  • uv run python -m pytest tests/processing/test_correction_log.py runs new tests for the stored values, round-trip, str() output, CORRECTION_ACTIONS membership and a polars filter on a saved log.
  • Replication script is byte-for-byte identical. I generated generate_corrections_script output from the same log (modify value, remove value, remove row, accept, and an unknown action) on the base branch and on this branch. cmp found no difference.
  • No action literals remain in src/: grep -rnE '"(modify value|remove value|remove row|accept)"' src matches only the enum definition, docstrings and onboarding help text.

Screenshots (if applicable) 📷

N/A. No UI changes.

Checklist ✅

  • I have run and tested my changes locally
  • I have limit this PR to less than 1000 lines of code change (if not, explain why)
  • I have updated/added tests to cover my changes (if applicable)
  • I have updated/added requirements to cover my changes (if applicable). N/A: no new dependencies
  • I have run linting and formatting on any code changes (if applicable)
  • I have updated the documentation (README, etc.) accordingly
  • I have reviewed and resolved any merge conflict

Reviewer Emoji Legend

:code: Meaning
😃👍💯 :smiley: :+1: :100: I like this...

...and I want the author to know it! This is a way to highlight positive parts of a code review.
⭐⭐⭐ :star: :star: :star: Important to fix before PR can be approved...

And I am providing reasons why it needs to be addressed as well as suggested improvements.
⭐⭐ :star: :star: Important to fix but non-blocking for PR approval...

And I am providing suggestions where it could be improved either in this PR or later.
⭐ :star: Give this some thought but non-blocking for PR approval...

...and consider this a suggestion, not a requirement.
❓ :question: I have a question.

This should be a fully formed question with sufficient information and context that requires a response.
📝 :memo: This is an explanatory note, fun fact, or relevant commentary that does not require any action.
⛏ :pick: This is a nitpick.

This does not require any changes and is often better left unsaid. This may include stylistic, formatting, or organization suggestions and should likely be prevented/enforced by linting if they really matter
♻️ :recycle: Suggestion for refactoring.

Should include enough context to be actionable and not be considered a nitpick.

🤖 Generated with Claude Code

@iabaako
iabaako requested a review from a team as a code owner September 30, 2026 19:59
@iabaako
iabaako requested a balanced review from Copilot September 30, 2026 20:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The refactor consistently preserves stored string behavior and includes focused compatibility coverage.

Review effort: Balanced
Findings: None

What changed in this PR

Replaces correction action constants with a typed Action StrEnum while preserving persisted values and behavior.

Changes:

  • Introduces Action and updates correction, form, view, and replication APIs.
  • Retains raw-string compatibility for persisted logs.
  • Adds enum compatibility and filtering tests.
File Description
src/​datasure/​processing/​correction_log.py Defines Action and data-changing actions.
src/​datasure/​processing/​corrections.py Uses typed actions throughout correction processing.
src/​datasure/​utils/​correction_form.py Types form actions with the enum.
src/​datasure/​views/​correction_view.py Updates the correction handler signature.
src/​datasure/​replication/​script_generators.py Uses enum members during script generation.
src/​datasure/​replication/​package_builder.py Excludes accept records using the enum.
tests/​processing/​test_correction_log.py Tests stored values and compatibility.
tests/​views/​test_view_logic.py Uses the canonical action vocabulary.
docs/​ARCHITECTURE.md Documents enum ownership.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@iabaako
iabaako added this pull request to stack #313 September 30, 2026 20:07
… StrEnum

Replace MODIFY_VALUE_ACTION, REMOVE_VALUE_ACTION, REMOVE_ROW_ACTION and
ACCEPT_ACTION with members of a single `Action` StrEnum in
correction_log.py, and define CORRECTION_ACTIONS from its members.
Signatures, CorrectionEntry and CorrectionFormState now take
`action: Action`.

Member values equal the strings already stored in corr_log_{alias}, and
StrEnum members compare equal to them, so existing logs load and replay
unchanged and the generated Stata script is byte-for-byte identical.
`_emit_stata` keeps `action: str` because it reads raw log values, and
coercing with `Action(...)` would raise on unknown values that are
skipped today.

Closes #310

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iabaako
iabaako force-pushed the refactor/310-action-strenum branch from c1dc6b8 to c3397c3 Compare October 9, 2026 22:27
@sonarqubecloud

sonarqubecloud Bot commented Oct 9, 2026

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Corrections: replace action string constants with an Action StrEnum in correction_log.py

2 participants