Your format_mrr_line function now handles the display side. But in your Stripe export, how do you currently spot churned customers?
I look for status = "churned" or mrr = 0. Sometimes a customer is still marked active but their MRR went to zero after a downgrade. I check both manually.
Python's or operator combines those two conditions in one expression. status == "churned" checks the text field. mrr == 0 catches the silent downgrade. Either condition being True makes the whole thing True:
status = "churned"
mrr = 49.0
flagged = (status == "churned") or (mrr == 0)
print(flagged) # TrueIf status is "active" and mrr is 49.0, does it check both conditions?
It does. "active" == "churned" is False. 49.0 == 0 is False. False or False is False — so the customer is not flagged. Python evaluates both sides with or. With and it would short-circuit after the first False. Here's the function:
def is_churned(status: str, mrr: float) -> bool:
result = (status == "churned") or (mrr == 0)
print(f"Churned: {result}")
return resultSo I can run this on every customer in my export and get a list of churn flags without opening a single spreadsheet filter.
Your spreadsheet filter is one if-condition in Python. Except Python runs it on 500 customers before your Excel tab finishes loading.
The churn cohort used to take me 15 minutes of manual filtering. This does it in one function call.
Watch mrr == 0 vs not mrr. Both work for zero, but not mrr also catches None and empty strings — which could silently flag customers with missing data. Use == 0 when you mean exactly zero.
A boolean is True or False. Comparison operators return booleans.
| Operator | Meaning | Example |
|---|---|---|
== | Equal | "churned" == "churned" → True |
!= | Not equal | "active" != "churned" → True |
> / < | Greater/less than | mrr > 0 |
or | Either True | a or b |
and | Both True | a and b |
not | Inverts | not True → False |
mrr == 0 checks exactly zero. not mrr also catches None and "" — use == 0 when you mean only zero.
Your format_mrr_line function now handles the display side. But in your Stripe export, how do you currently spot churned customers?
I look for status = "churned" or mrr = 0. Sometimes a customer is still marked active but their MRR went to zero after a downgrade. I check both manually.
Python's or operator combines those two conditions in one expression. status == "churned" checks the text field. mrr == 0 catches the silent downgrade. Either condition being True makes the whole thing True:
status = "churned"
mrr = 49.0
flagged = (status == "churned") or (mrr == 0)
print(flagged) # TrueIf status is "active" and mrr is 49.0, does it check both conditions?
It does. "active" == "churned" is False. 49.0 == 0 is False. False or False is False — so the customer is not flagged. Python evaluates both sides with or. With and it would short-circuit after the first False. Here's the function:
def is_churned(status: str, mrr: float) -> bool:
result = (status == "churned") or (mrr == 0)
print(f"Churned: {result}")
return resultSo I can run this on every customer in my export and get a list of churn flags without opening a single spreadsheet filter.
Your spreadsheet filter is one if-condition in Python. Except Python runs it on 500 customers before your Excel tab finishes loading.
The churn cohort used to take me 15 minutes of manual filtering. This does it in one function call.
Watch mrr == 0 vs not mrr. Both work for zero, but not mrr also catches None and empty strings — which could silently flag customers with missing data. Use == 0 when you mean exactly zero.
A boolean is True or False. Comparison operators return booleans.
| Operator | Meaning | Example |
|---|---|---|
== | Equal | "churned" == "churned" → True |
!= | Not equal | "active" != "churned" → True |
> / < | Greater/less than | mrr > 0 |
or | Either True | a or b |
and | Both True | a and b |
not | Inverts | not True → False |
mrr == 0 checks exactly zero. not mrr also catches None and "" — use == 0 when you mean only zero.
Omar reviews his churn cohort every Monday — customers where status is 'churned' or MRR has gone to zero after a downgrade. Write `is_churned(status, mrr)` that returns `True` if status equals 'churned' or mrr equals 0, and `False` otherwise — for example, `is_churned('active', 49.0)` should return `False`.
Tap each step for scaffolded hints.
No blank-editor panic.