2626from sift_client .sift_types .run import Run
2727
2828if TYPE_CHECKING :
29+ from collections .abc import Iterable
30+
2931 from sift_client .client import SiftClient
3032 from sift_client .sift_types .job import Job
3133
@@ -318,6 +320,24 @@ def _parse_csv_detect_response(proto) -> CsvImportConfig:
318320 return csv_config
319321
320322
323+ def _infer_time_column (columns : Iterable [tuple [str , ChannelDataType , str ]]) -> str | None :
324+ """Find a likely time column from a sequence of (name, data_type, path) tuples.
325+
326+ The backend only detects arrow timestamp types. This falls back to the first
327+ integer column whose name starts with "time".
328+ """
329+ _integer_types = {
330+ ChannelDataType .INT_32 ,
331+ ChannelDataType .INT_64 ,
332+ ChannelDataType .UINT_32 ,
333+ ChannelDataType .UINT_64 ,
334+ }
335+ for name , data_type , path in columns :
336+ if data_type in _integer_types and name .lower ().startswith ("time" ):
337+ return path
338+ return None
339+
340+
321341def _parse_parquet_detect_response (
322342 proto , filename : str , footer_offset : int , footer_length : int
323343) -> ParquetFlatDatasetImportConfig | ParquetSingleChannelPerRowImportConfig :
@@ -332,29 +352,27 @@ def _parse_parquet_detect_response(
332352 dc for dc in parquet_config .data_columns if dc .path != time_path
333353 ]
334354 else :
335- # The backend only detects arrow timestamp types. Fall back to
336- # an integer column whose name starts with "time".
337- _integer_types = {
338- ChannelDataType .INT_32 ,
339- ChannelDataType .INT_64 ,
340- ChannelDataType .UINT_32 ,
341- ChannelDataType .UINT_64 ,
342- }
343- match = None
344- for dc in parquet_config .data_columns :
345- if dc .data_type in _integer_types and dc .name .lower ().startswith ("time" ):
346- match = dc
347- break
348- if match is not None :
349- parquet_config .time_column = ParquetTimeColumn (path = match .path )
355+ inferred = _infer_time_column (
356+ (dc .name , dc .data_type , dc .path ) for dc in parquet_config .data_columns
357+ )
358+ if inferred is not None :
359+ parquet_config .time_column = ParquetTimeColumn (path = inferred )
350360 parquet_config .data_columns = [
351- c for c in parquet_config .data_columns if c .path != match . path
361+ c for c in parquet_config .data_columns if c .path != inferred
352362 ]
353363 return parquet_config
354364 elif proto .HasField ("single_channel_per_row" ):
355- return ParquetSingleChannelPerRowImportConfig ._from_proto (
365+ parquet_config = ParquetSingleChannelPerRowImportConfig ._from_proto (
356366 proto , footer_offset = footer_offset , footer_length = footer_length
357367 )
368+ if not parquet_config .time_column .path :
369+ inferred = _infer_time_column (
370+ (col .column_config .name , ChannelDataType (col .column_config .data_type ), col .path )
371+ for col in proto .single_channel_per_row .columns
372+ )
373+ if inferred is not None :
374+ parquet_config .time_column = ParquetTimeColumn (path = inferred )
375+ return parquet_config
358376 raise ValueError (f"Unsupported parquet layout in DetectConfig response for '{ filename } '." )
359377
360378
0 commit comments