@@ -26,11 +26,24 @@ def source_exists(cursor, table_name_to_check: str) -> bool:
2626 return cursor .execute (query , (table_name_to_check ,)).fetchone () is not None
2727
2828
29- def generate_timestamp_to_localtimestamp_clause (timestamp_columns ) -> str :
29+ def generate_timestamp_to_localtimestamp_clause (timestamp_columns : list [ str ] ) -> str :
3030 if not timestamp_columns :
3131 return ""
3232
33- clause = "," .join ([f"datetime({ c } , 'localtime') as { c } _localtime" for c in timestamp_columns ])
33+ clause = "," .join ([f"datetime(T.{ c } , 'localtime') as { c } _localtime" for c in timestamp_columns ])
34+
35+ return clause
36+
37+
38+ def generate_timestamp_to_relative_time_clause (default_order_by : str ) -> str :
39+ if not default_order_by :
40+ return ""
41+
42+ START_TIME = "E.created_at"
43+
44+ clause = (
45+ f"(unixepoch({ default_order_by } ) - unixepoch({ START_TIME } ))/3600.0 as hours_since_experiment_started"
46+ )
3447
3548 return clause
3649
@@ -73,7 +86,7 @@ def create_experiment_clause(
7386 existing_placeholders = existing_placeholders | {
7487 f"experiment{ i } " : experiment for i , experiment in enumerate (experiments )
7588 }
76- return f"experiment IN ({ quoted_experiments } )" , existing_placeholders
89+ return f"T. experiment IN ({ quoted_experiments } )" , existing_placeholders
7790
7891
7992def create_timespan_clause (
@@ -82,15 +95,15 @@ def create_timespan_clause(
8295 if start_time is not None and end_time is not None :
8396 existing_placeholders ["start_time" ] = start_time
8497 existing_placeholders ["end_time" ] = end_time
85- return f"{ time_column } >= :start_time AND { time_column } <= :end_time" , existing_placeholders
98+ return f"T. { time_column } >= :start_time AND { time_column } <= :end_time" , existing_placeholders
8699
87100 elif start_time is not None :
88101 existing_placeholders ["start_time" ] = start_time
89- return f"{ time_column } >= :start_time" , existing_placeholders
102+ return f"T. { time_column } >= :start_time" , existing_placeholders
90103
91104 elif end_time is not None :
92105 existing_placeholders ["end_time" ] = end_time
93- return f"{ time_column } <= :end_time" , existing_placeholders
106+ return f"T. { time_column } <= :end_time" , existing_placeholders
94107 else :
95108 raise ValueError
96109
@@ -101,20 +114,24 @@ def create_sql_query(
101114 existing_placeholders : dict [str , str ],
102115 where_clauses : list [str ] | None = None ,
103116 order_by_col : str | None = None ,
117+ has_experiment : bool = False ,
104118) -> tuple [str , dict [str , str ]]:
105119 """
106120 Constructs an SQL query with SELECT, FROM, WHERE, and ORDER BY clauses.
107121 """
108122 # Base SELECT and FROM clause
109- query = f"SELECT { ', ' .join (selects )} FROM ({ table_or_subquery } )"
123+ query = f"SELECT { ', ' .join (selects )} FROM ({ table_or_subquery } ) T"
124+
125+ if has_experiment :
126+ query += "JOIN experiment E ON E.experiment = T.experiment"
110127
111128 # Add WHERE clause if provided
112129 if where_clauses :
113130 query += f" WHERE { ' AND ' .join (where_clauses )} "
114131
115132 # Add ORDER BY clause if provided
116133 if order_by_col :
117- query += f' ORDER BY "{ order_by_col } "'
134+ query += f' ORDER BY "T. { order_by_col } "'
118135
119136 return query , existing_placeholders
120137
@@ -202,6 +219,9 @@ def export_experiment_data(
202219 experiment_clause , placeholders = create_experiment_clause (experiments , placeholders )
203220 where_clauses .append (experiment_clause )
204221
222+ if dataset .has_experiment and dataset .default_order_by :
223+ selects .append (generate_timestamp_to_relative_time_clause (dataset .default_order_by ))
224+
205225 if dataset .timestamp_columns and (start_time or end_time ):
206226 assert dataset .default_order_by is not None
207227 timespan_clause , placeholders = create_timespan_clause (
0 commit comments