Skip to content

fix(reports): humanize day-of-month + day-of-week crontabs as OR - #43307

Open
aminghadersohi wants to merge 2 commits into
apache:masterfrom
aminghadersohi:aminghadersohi/cron-description-or-semantics
Open

fix(reports): humanize day-of-month + day-of-week crontabs as OR#43307
aminghadersohi wants to merge 2 commits into
apache:masterfrom
aminghadersohi:aminghadersohi/cron-description-or-semantics

Conversation

@aminghadersohi

Copy link
Copy Markdown
Contributor

SUMMARY

The Schedule column on Alerts & Reports shows a human-readable rendering of the report's crontab, produced by ReportSchedule.crontab_humanizedcron_descriptor.get_description(). When a schedule restricts both the day-of-month and the day-of-week fields, that description is wrong.

0 9 7-11,19-23 * 2 renders as:

At 09:00 AM, on day 7 through 11 and 19 through 23 of the month, only on Tuesday

The comma plus "only on" reads as an intersection — "the 7th–11th and 19th–23rd, but only when that day is a Tuesday". POSIX cron does the opposite: when both fields are restricted they are OR'd. croniter, which selects the actual fire times in superset/tasks/cron_util.py, implements that correctly, so the report also fires on every Tuesday outside those two ranges. Verified against croniter: for the expression above the first fire time after 2026-09-01 is 2026-09-01 09:00 — a Tuesday that is in neither day-of-month range.

So the picker is right and the description is wrong. The frontend cron picker (react-js-cron, via CronPicker) already renders this correctly: its locale sets prefixWeekDaysForMonthAndYearPeriod: 'or', so the builder shows "… on day 7-11,19-23 or Tuesday". Only the backend-generated text disagreed, which is exactly the mismatch users hit — they configure a schedule in the picker and then read a contradictory description in the list.

Rather than swapping out cron_descriptor (which is otherwise fine and handles L, W, #n, i18n, etc.), this adds a small get_cron_description() wrapper in superset/tasks/cron_util.py — the module that already owns cron interpretation via croniter. It only changes the output for the ambiguous case: when neither the day-of-month nor the day-of-week field is */?, the day-of-week clause is rejoined with "or". Every other expression is passed through cron_descriptor untouched.

crontab before after
0 9 7-11,19-23 * 2 …of the month, only on Tuesday …of the month, or on Tuesday
0 9 1,15 * 1-5 …of the month, Monday through Friday …of the month, or Monday through Friday
0 9 L * 5 on the last day of the month, only on Friday on the last day of the month, or on Friday
0 9 * * 2 only on Tuesday (unchanged)
0 9 7-11 * * on day 7 through 11 of the month (unchanged)

Known limitations: the rewrite is applied to standard 5-field expressions only (what the picker and the API validator produce); 6-field expressions fall through to the unmodified cron_descriptor output because the field offsets are ambiguous. The "only " prefix strip is English-specific — cron_descriptor is invoked with its default en_US locale here, and if the prefix isn't present the clause is emitted verbatim, so a different locale degrades to ", or " rather than breaking.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

No UI code changed — this is the text returned by the crontab_humanized field of GET /api/v1/report/, rendered verbatim in the Schedule column. The before/after strings are in the table above.

TESTING INSTRUCTIONS

Regression tests in tests/unit_tests/tasks/test_cron_util.py:

  • test_get_cron_description — parametrized over the ambiguous and unambiguous cases; the ambiguous rows fail on master (assert '…of the month, only on Tuesday' == '…of the month, or on Tuesday').
  • test_get_cron_description_matches_fire_times — pins the description against what croniter actually schedules, so the two can't drift apart again.
pytest tests/unit_tests/tasks/test_cron_util.py tests/unit_tests/reports/ -q
281 passed

Manually: create a report with schedule type "CRON Schedule" and crontab 0 9 7-11,19-23 * 2, then look at the Schedule column on the Alerts & Reports list. It should read "At 09:00 AM, on day 7 through 11 and 19 through 23 of the month, or on Tuesday", matching what the recurring picker shows for the same expression.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

cron_descriptor joins a restricted day-of-month and a restricted
day-of-week with a comma, which reads as an intersection: "0 9 7-11 * 2"
renders as "At 09:00 AM, on day 7 through 11 of the month, only on
Tuesday". POSIX cron - and croniter, which selects the fire times -
unions the two fields instead, so the report also runs on every Tuesday
outside that range. Join the clauses with "or" so the Schedule column
matches when the report actually fires.
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.66%. Comparing base (097c99b) to head (e09b7a0).

Files with missing lines Patch % Lines
superset/tasks/cron_util.py 43.75% 7 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43307      +/-   ##
==========================================
- Coverage   66.67%   66.66%   -0.01%     
==========================================
  Files        2876     2876              
  Lines      164007   164020      +13     
  Branches    37834    37836       +2     
==========================================
  Hits       109347   109347              
- Misses      52514    52524      +10     
- Partials     2146     2149       +3     
Flag Coverage Δ
hive 38.10% <22.22%> (-0.01%) ⬇️
mysql 57.75% <50.00%> (-0.01%) ⬇️
postgres 57.78% <50.00%> (-0.01%) ⬇️
presto 40.03% <22.22%> (-0.01%) ⬇️
python 59.17% <50.00%> (-0.01%) ⬇️
sqlite 57.42% <50.00%> (-0.01%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aminghadersohi
aminghadersohi marked this pull request as ready for review August 19, 2026 03:38
@dosubot dosubot Bot added the alert-reports Namespace | Anything related to the Alert & Reports feature label Aug 19, 2026
Comment on lines +50 to +52
fields = cron.split()
if len(fields) != 5:
return description

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.

Suggestion: Expressions are only corrected when they contain exactly five fields. However, validate_crontab and cron_schedule_window both delegate to croniter, which accepts six- and seven-field expressions, including schedules with restricted day-of-month and day-of-week fields. Those schedules bypass the OR replacement and continue to display the original intersection-like description. Either support all cron formats accepted by the scheduler or explicitly reject non-five-field expressions during validation. [api mismatch]

Severity Level: Major ⚠️
- ⚠️ API-accepted extended cron schedules show misleading descriptions.
- ⚠️ Report list and detail views disagree with scheduler fire times.
- ⚠️ Users may misinterpret alert or report execution dates.

Use CodeAnt Skill

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/tasks/cron_util.py
**Line:** 50:52
**Comment:**
	*Api Mismatch: Expressions are only corrected when they contain exactly five fields. However, `validate_crontab` and `cron_schedule_window` both delegate to `croniter`, which accepts six- and seven-field expressions, including schedules with restricted day-of-month and day-of-week fields. Those schedules bypass the OR replacement and continue to display the original intersection-like description. Either support all cron formats accepted by the scheduler or explicitly reject non-five-field expressions during validation.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. The current implementation of get_cron_description in superset/tasks/cron_util.py explicitly checks for exactly five fields (if len(fields) != 5: return description), which causes it to ignore extended cron expressions (six or seven fields) that croniter supports. This leads to misleading descriptions for those schedules.

To resolve this, you can update the validation logic to support extended cron formats. Since croniter handles the parsing, you can allow expressions with more fields and ensure the logic correctly identifies the day-of-month and day-of-week fields based on their position in the cron string.

Would you like me to fetch all other comments on this PR to validate them and implement a minimal fix for the rest as well?

superset/tasks/cron_util.py

fields = cron.split()
    if len(fields) < 5 or len(fields) > 7:
        return description

    day_of_month, day_of_week = fields[2], fields[4]

@bito-code-review

bito-code-review Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #9a5f44

Actionable Suggestions - 0
Review Details
  • Files reviewed - 3 · Commit Range: e0a1361..e09b7a0
    • superset/reports/models.py
    • superset/tasks/cron_util.py
    • tests/unit_tests/tasks/test_cron_util.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@aminghadersohi
aminghadersohi requested review from gabotorresruiz and removed request for eschutho August 19, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

alert-reports Namespace | Anything related to the Alert & Reports feature size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant