Spaces:
Sleeping
Sleeping
| import sqlite3 | |
| from .base import BaseTask | |
| class ChurnAnalysisTask(BaseTask): | |
| """Task 3 — Hard: Find users who placed exactly 3 orders and then churned.""" | |
| task_id = "churn_analysis" | |
| difficulty = "hard" | |
| max_steps = 20 | |
| question = "Find the email addresses of users who placed exactly 3 orders and then never ordered again (churned after their 3rd purchase). Return as a comma-separated list." | |
| relevant_tables = ["users", "orders"] | |
| sql_hint = "CTE with COUNT and HAVING" | |
| def compute_ground_truth(self, conn: sqlite3.Connection) -> None: | |
| result = conn.execute(""" | |
| WITH order_counts AS ( | |
| SELECT user_id, COUNT(*) AS total_orders, | |
| MAX(created_at) AS last_order_date | |
| FROM orders | |
| WHERE status = 'completed' | |
| GROUP BY user_id | |
| HAVING COUNT(*) = 3 | |
| ), | |
| churned AS ( | |
| SELECT oc.user_id | |
| FROM order_counts oc | |
| WHERE oc.last_order_date < DATE('now', '-90 days') | |
| ) | |
| SELECT u.email | |
| FROM users u | |
| JOIN churned c ON u.id = c.user_id | |
| """).fetchall() | |
| self.ground_truth = {row[0].lower() for row in result} | |
| def grade(self, submitted_answer: str) -> float: | |
| if not submitted_answer.strip(): | |
| return 0.0 | |
| submitted = {e.strip().lower() for e in submitted_answer.split(",") if "@" in e} | |
| if not submitted: | |
| return 0.0 | |
| correct = {e.lower() for e in self.ground_truth} | |
| tp = len(submitted & correct) | |
| if tp == 0: | |
| return 0.0 | |
| precision = tp / len(submitted) if submitted else 0 | |
| recall = tp / len(correct) if correct else 0 | |
| if precision + recall == 0: | |
| return 0.0 | |
| f1 = 2 * precision * recall / (precision + recall) | |
| return round(f1, 3) | |