Skip to content

feat(experiments): support time units in conversion windows #30871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 9, 2025

Conversation

andehen
Copy link
Contributor

@andehen andehen commented Apr 7, 2025

Problem

We currently only support specifying hours. Cumbersome to enter days f.ex.

Changes

  • Add support for specifying a time unit
  • split out into separate component

How did you test this code?

  • modified tests to work with new format, tests passes
  • manually set and unset time windows and verified content in the db

@andehen andehen requested a review from a team April 7, 2025 12:50
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

The changes introduce multi-unit conversion window support for experiment metrics by refactoring both frontend and backend logic to replace the old hour-only configuration.

  • Updated /frontend/src/queries/schema/schema-general.ts and /posthog/schema.py to replace time_window_hours with conversion_window and conversion_window_unit.
  • Refactored /frontend/src/scenes/experiments/ExperimentMetricForm.tsx by extracting conversion window handling into a new component at /frontend/src/scenes/experiments/ExperimentMetricConversionWindowFilter.tsx.
  • Modified /posthog/hogql_queries/experiments/funnel_query_utils.py and /posthog/hogql_queries/experiments/experiment_query_runner.py to incorporate the new conversion window logic.
  • Added utility conversion_window_to_seconds in /posthog/hogql_queries/experiments/base_query_utils.py with corresponding test updates.

9 file(s) reviewed, 1 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +53 to +60
onChange={(value) =>
handleSetMetric({
...metric,
conversion_window: value === 'experiment_duration' ? undefined : 14,
conversion_window_unit:
value === 'experiment_duration' ? undefined : FunnelConversionWindowTimeUnit.Day,
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Switching to time_window mode always resets conversion_window to 14 and unit to Day. Consider preserving the user's previous conversion_window_unit if available.

@andehen andehen changed the title feature(experiments): support time units in conversion windows feat(experiments): support time units in conversion windows Apr 7, 2025
Copy link
Contributor

github-actions bot commented Apr 7, 2025

Size Change: +400 B (0%)

Total Size: 13.2 MB

ℹ️ View Unchanged
Filename Size Change
frontend/dist/toolbar.js 13.2 MB +400 B (0%)

compressed-size-action

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 1)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

2 snapshot changes in total. 0 added, 2 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

2 snapshot changes in total. 0 added, 2 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 7)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

Comment on lines +18 to +24
const options: LemonSelectOption<FunnelConversionWindowTimeUnit>[] = Object.keys(TIME_INTERVAL_BOUNDS).map(
(unit) => ({
label: capitalizeFirstLetter(pluralize(metric.conversion_window ?? 72, unit, `${unit}s`, false)),
value: unit as FunnelConversionWindowTimeUnit,
})
)
const intervalBounds = TIME_INTERVAL_BOUNDS[metric.conversion_window_unit ?? FunnelConversionWindowTimeUnit.Day]
Copy link
Contributor

Choose a reason for hiding this comment

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

⛏️ Complete and absolute nitpick: you could extract these as functions that take an ExperimentMetric argument. Then, they could also be moved out into FunnelConversionWindowFilter scene, removing the TIME_INTERVAL_BOUNDS import (hiding that away).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As the functions are only used here and are pretty straight forward, I think I prefer leaving them there for now.

)}
</div>
</div>
<ExperimentMetricConversionWindowFilter metric={metric} handleSetMetric={handleSetMetric} />
Copy link
Contributor

Choose a reason for hiding this comment

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

terry crews chefs kiss

Comment on lines 74 to 91
def conversion_window_to_seconds(conversion_window: int, conversion_window_unit: FunnelConversionWindowTimeUnit) -> int:
match conversion_window_unit:
case FunnelConversionWindowTimeUnit.SECOND:
multiplier = 1
case FunnelConversionWindowTimeUnit.MINUTE:
multiplier = 60
case FunnelConversionWindowTimeUnit.HOUR:
multiplier = 60 * 60
case FunnelConversionWindowTimeUnit.DAY:
multiplier = 24 * 60 * 60
case FunnelConversionWindowTimeUnit.WEEK:
multiplier = 7 * 24 * 60 * 60
case FunnelConversionWindowTimeUnit.MONTH:
multiplier = 30 * 24 * 60 * 60
case _:
raise ValueError(f"Unsupported conversion window unit: {conversion_window_unit}")

return conversion_window * multiplier
Copy link
Contributor

Choose a reason for hiding this comment

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

When I see a switch/case, I think could it be better?. We could use if and make this hilariously recursive:

def conversion_window_to_seconds(conversion_window: int, conversion_window_unit: FunnelConversionWindowTimeUnit) -> int:
    # Base case
    if conversion_window_unit == FunnelConversionWindowTimeUnit.SECOND:
        return conversion_window
    elif conversion_window_unit == FunnelConversionWindowTimeUnit.MINUTE:
        return conversion_window_to_seconds(conversion_window * 60, FunnelConversionWindowTimeUnit.SECOND)
    elif conversion_window_unit == FunnelConversionWindowTimeUnit.HOUR:
        return conversion_window_to_seconds(conversion_window * 60, FunnelConversionWindowTimeUnit.MINUTE)
    elif conversion_window_unit == FunnelConversionWindowTimeUnit.DAY:
        return conversion_window_to_seconds(conversion_window * 24, FunnelConversionWindowTimeUnit.HOUR)
    elif conversion_window_unit == FunnelConversionWindowTimeUnit.WEEK:
        return conversion_window_to_seconds(conversion_window * 7, FunnelConversionWindowTimeUnit.DAY)
    elif conversion_window_unit == FunnelConversionWindowTimeUnit.MONTH:
        return conversion_window_to_seconds(conversion_window * 30, FunnelConversionWindowTimeUnit.DAY)
    else:
        raise ValueError(f"Unsupported conversion window unit: {conversion_window_unit}")

or try to make it shorter with a map:

def conversion_window_to_seconds(conversion_window: int, conversion_window_unit: FunnelConversionWindowTimeUnit) -> int:
    unit_multipliers = {
        FunnelConversionWindowTimeUnit.SECOND: 1,
        FunnelConversionWindowTimeUnit.MINUTE: 60,
        FunnelConversionWindowTimeUnit.HOUR: 3600,
        FunnelConversionWindowTimeUnit.DAY: 86400,
        FunnelConversionWindowTimeUnit.WEEK: 604800,
        FunnelConversionWindowTimeUnit.MONTH: 2592000,
    }
    if conversion_window_unit not in unit_multipliers:
        raise ValueError(f"Unsupported conversion window unit: {conversion_window_unit}")
    return conversion_window * unit_multipliers[conversion_window_unit]

and then I realize that sometimes a case is 🆗. But a recursive solution could be fun. Just imagine the face of someone finding that code?

Copy link
Contributor Author

@andehen andehen Apr 9, 2025

Choose a reason for hiding this comment

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

Recursion would be fun. But it felt a little to verbose in this case. I went with the map solution, that felt best. 7656d52 (#30871)

Copy link
Contributor

Choose a reason for hiding this comment

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

I was hoping for some good ol' recursion :(

Copy link
Contributor

@rodrigoi rodrigoi left a comment

Choose a reason for hiding this comment

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

Yoda Approves

@andehen andehen enabled auto-merge (squash) April 9, 2025 05:07
@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

3 snapshot changes in total. 0 added, 3 modified, 0 deleted:

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 5)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@andehen andehen force-pushed the experiments/fix-conversion-window-config branch from 109e6b7 to 1167cc2 Compare April 9, 2025 06:49
@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 5)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 5)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@andehen andehen merged commit cd1c0d8 into master Apr 9, 2025
109 checks passed
@andehen andehen deleted the experiments/fix-conversion-window-config branch April 9, 2025 07:29
hpouillot pushed a commit that referenced this pull request Apr 9, 2025
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
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.

3 participants