From ce199496ff7b99f98d747022803c1a7a486ced84 Mon Sep 17 00:00:00 2001 From: Yusuke Watanabe Date: Mon, 27 Apr 2026 09:23:15 +0900 Subject: [PATCH] cleanup(dt): drop shadowed local _linspace.py / _normalize_timestamp.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scitex.dt is a backward-compat alias of scitex.datetime — its __init__.py imports everything from scitex.datetime, leaving the local _linspace.py and _normalize_timestamp.py shadowed dead code (verified: no module reads them by direct path). Drop them along with the empty _skills/ dir. Verified: hasattr(scitex.dt, 'linspace' / 'to_datetime') == True. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/scitex/dt/_linspace.py | 131 -------- src/scitex/dt/_normalize_timestamp.py | 436 -------------------------- src/scitex/dt/_skills/SKILL.md | 41 --- src/scitex/dt/_skills/alias.md | 30 -- src/scitex/dt/_skills/dt-alias.md | 61 ---- 5 files changed, 699 deletions(-) delete mode 100755 src/scitex/dt/_linspace.py delete mode 100755 src/scitex/dt/_normalize_timestamp.py delete mode 100644 src/scitex/dt/_skills/SKILL.md delete mode 100644 src/scitex/dt/_skills/alias.md delete mode 100644 src/scitex/dt/_skills/dt-alias.md diff --git a/src/scitex/dt/_linspace.py b/src/scitex/dt/_linspace.py deleted file mode 100755 index a1f2d1a0a..000000000 --- a/src/scitex/dt/_linspace.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# Timestamp: "2025-04-23 10:42:43 (ywatanabe)" -# File: /ssh:sp:/home/ywatanabe/proj/scitex_repo/src/scitex/dt/_linspace.py -# ---------------------------------------- -import os - -__FILE__ = "./src/scitex/dt/_linspace.py" -__DIR__ = os.path.dirname(__FILE__) -# ---------------------------------------- - -import datetime -from datetime import timedelta -from typing import Any, List, Optional, Union - -import numpy as np - - -def linspace( - start_dt: datetime.datetime, - end_dt: datetime.datetime, - n_samples: Optional[int] = None, - sampling_rate: Optional[float] = None, -) -> np.ndarray: - """ - Create a linearly spaced array between two datetime objects. - - Args: - start_dt: Starting datetime object - end_dt: Ending datetime object - n_samples: Number of samples to create (mutually exclusive with sampling_rate) - sampling_rate: Sampling rate in Hz (mutually exclusive with n_samples) - - Returns: - Array of datetime objects evenly spaced between start_dt and end_dt - """ - # Type checking - if not isinstance(start_dt, datetime.datetime): - raise TypeError(f"start_dt must be a datetime object, got {type(start_dt)}") - - if not isinstance(end_dt, datetime.datetime): - raise TypeError(f"end_dt must be a datetime object, got {type(end_dt)}") - - if n_samples is not None and not isinstance(n_samples, (int, float)): - raise TypeError(f"n_samples must be a number, got {type(n_samples)}") - - if sampling_rate is not None and not isinstance(sampling_rate, (int, float)): - raise TypeError(f"sampling_rate must be a number, got {type(sampling_rate)}") - - if start_dt >= end_dt: - raise ValueError("start_dt must be earlier than end_dt") - - duration_seconds = (end_dt - start_dt).total_seconds() - - if n_samples is not None and sampling_rate is not None: - raise ValueError("Provide either n_samples or sampling_rate, not both") - - if n_samples is None and sampling_rate is None: - raise ValueError("Either n_samples or sampling_rate must be provided") - - if sampling_rate is not None: - if sampling_rate <= 0: - raise ValueError("sampling_rate must be positive") - n_samples = int(duration_seconds * sampling_rate) + 1 - else: - if n_samples <= 0: - raise ValueError("n_samples must be positive") - - # Create linear space in seconds - seconds_array = np.linspace(0, duration_seconds, n_samples) - - # Convert to datetime objects - datetime_array = np.array( - [start_dt + timedelta(seconds=float(sec)) for sec in seconds_array] - ) - - return datetime_array - - -# #!/usr/bin/env python3 -# # -*- coding: utf-8 -*- -# # Timestamp: "2025-04-23 10:38:20 (ywatanabe)" -# # File: /ssh:sp:/home/ywatanabe/proj/scitex_repo/src/scitex/dt/_linspace.py -# # ---------------------------------------- -# import os -# __FILE__ = ( -# "./src/scitex/dt/_linspace.py" -# ) -# __DIR__ = os.path.dirname(__FILE__) -# # ---------------------------------------- - -# import numpy as np -# from datetime import timedelta - -# def linspace(start_dt, end_dt, n_samples=None, sampling_rate=None): -# """ -# Create a linearly spaced array between two datetime objects. - -# Args: -# start_dt: Starting datetime object -# end_dt: Ending datetime object -# n_samples: Number of samples to create (mutually exclusive with sampling_rate) -# sampling_rate: Sampling rate in Hz (mutually exclusive with n_samples) - -# Returns: -# Array of datetime objects evenly spaced between start_dt and end_dt -# """ - -# duration_seconds = (end_dt - start_dt).total_seconds() - -# if n_samples is not None and sampling_rate is not None: -# raise ValueError("Provide either n_samples or sampling_rate, not both") - -# if n_samples is None and sampling_rate is None: -# raise ValueError("Either n_samples or sampling_rate must be provided") - -# if sampling_rate is not None: -# n_samples = int(duration_seconds * sampling_rate) + 1 - - -# # Create linear space in seconds -# seconds_array = np.linspace(0, duration_seconds, n_samples) - -# # Convert to datetime objects -# datetime_array = np.array([start_dt + timedelta(seconds=float(sec)) for sec in seconds_array]) - -# return datetime_array - -# # EOF - -# EOF diff --git a/src/scitex/dt/_normalize_timestamp.py b/src/scitex/dt/_normalize_timestamp.py deleted file mode 100755 index c1157a638..000000000 --- a/src/scitex/dt/_normalize_timestamp.py +++ /dev/null @@ -1,436 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# Timestamp: "2025-09-21 20:32:23 (ywatanabe)" -# File: /ssh:sp:/home/ywatanabe/proj/scitex_repo/src/scitex/ml/classification/_normalize_timestamp.py -# ---------------------------------------- -from __future__ import annotations - -import os - -__FILE__ = __file__ -__DIR__ = os.path.dirname(__FILE__) -# ---------------------------------------- - -""" -Timestamp Standardization Utilities - -Functionality: -- Standardizes timestamps to consistent format defined in CONFIG.FORMATS.TIMESTAMP -- Handles various input formats (datetime objects, strings, timestamps) -- Provides UTC normalization -- Ensures consistent timestamp formatting across the codebase - -Input formats supported: -- datetime objects (with or without timezone) -- Unix timestamps (int/float) -- Various string formats - -Output: -- Standardized timestamp strings in format: "%Y-%m-%d %H:%M:%S.%f" -- UTC normalized timestamps -- Validation utilities - -Prerequisites: -- CONFIG.FORMATS.TIMESTAMP for standard format -""" - -"""Imports""" -import argparse -from datetime import datetime, timezone -from typing import Union - -import scitex as stx - -"""Parameters""" -CONFIG = stx.io.load_configs() - -# Get standard format from config -STANDARD_FORMAT = CONFIG.FORMATS.TIMESTAMP or "%Y-%m-%d %H:%M:%S" - - -# Common alternative formats to try when parsing -ALTERNATIVE_FORMATS = [ - "%Y-%m-%dT%H:%M:%S.%f", - "%Y-%m-%dT%H:%M:%S", # ISO 8601 with T (no microseconnds) - "%Y-%m-%d %H:%M:%S.%f", - "%Y-%m-%d %H:%M:%S", - "%Y/%m/%d %H:%M:%S.%f", - "%Y/%m/%d %H:%M:%S", - "%d-%m-%Y %H:%M:%S.%f", - "%d-%m-%Y %H:%M:%S", - "%d/%m/%Y %H:%M:%S.%f", - "%d/%m/%Y %H:%M:%S", - "%d/%m/%Y, %H:%M:%S", # Format used in REC_START - "%Y%m%d %H:%M:%S.%f", - "%Y%m%d %H:%M:%S", - "%Y-%m-%d_%H:%M:%S.%f", - "%Y-%m-%d_%H:%M:%S", -] - -"""Functions & Classes""" - - -def normalize_timestamp( - timestamp: Union[datetime, str, int, float], - return_as: str = "str", - normalize_utc: bool = True, -) -> Union[str, datetime, float]: - """ - Standardize any timestamp format to requested output type. - - Parameters - ---------- - timestamp : datetime, str, int, or float - Timestamp in any supported format - return_as : str - Output format: "str" (default), "datetime", or "timestamp" - normalize_utc : bool - If True, normalize to UTC timezone - - Returns - ------- - str, datetime, or float - Standardized timestamp in requested format: - - "str": String in CONFIG.FORMATS.TIMESTAMP format - - "datetime": datetime object - - "timestamp": Unix timestamp (float) - - Examples - -------- - >>> from datetime import datetime - >>> dt = datetime(2010, 6, 18, 10, 15, 0) - - >>> normalize_timestamp(dt, return_as="str") - "2010-06-18 10:15:00.000000" - - >>> normalize_timestamp(dt, return_as="datetime") - datetime(2010, 6, 18, 10, 15, 0, tzinfo=timezone.utc) - - >>> normalize_timestamp(dt, return_as="timestamp") - 1276856100.0 - - >>> normalize_timestamp("2010/06/18 10:15:00", return_as="str") - "2010-06-18 10:15:00.000000" - """ - # Convert to datetime object - dt = to_datetime(timestamp) - - # Normalize to UTC if requested - if normalize_utc: - if dt.tzinfo is None: - dt = dt.replace(tzinfo=timezone.utc) - else: - dt = dt.astimezone(timezone.utc) - - # Return in requested format - if return_as == "str": - return dt.strftime(STANDARD_FORMAT) - elif return_as == "datetime": - return dt - elif return_as == "timestamp": - return dt.timestamp() - else: - raise ValueError( - f"return_as must be 'str', 'datetime', or 'timestamp', got: {return_as}" - ) - - -def to_datetime(timestamp: Union[datetime, str, int, float]) -> datetime: - """ - Convert various timestamp formats to datetime object. - - Parameters - ---------- - timestamp : datetime, str, int, or float - Timestamp in any supported format - - Returns - ------- - datetime - Datetime object - """ - # Already datetime - if isinstance(timestamp, datetime): - return timestamp - - # Unix timestamp (int/float) - elif isinstance(timestamp, (int, float)): - return datetime.fromtimestamp(timestamp, tz=timezone.utc) - - # String format - elif isinstance(timestamp, str): - # Handle nanosecond precision by truncating to microseconds - if "." in timestamp and len(timestamp.split(".")[-1]) > 6: - parts = timestamp.split(".") - # Keep only first 6 digits of fractional seconds - truncated_microseconds = parts[-1][:6] - # Handle cases where there might be additional text after microseconds - if not truncated_microseconds.isdigit(): - # Extract just the digit portion - import re - - digits = re.match(r"(\d+)", parts[-1]) - if digits: - truncated_microseconds = digits.group(1)[:6] - timestamp = ".".join(parts[:-1] + [truncated_microseconds]) - - # Try parsing with various formats - for fmt in ALTERNATIVE_FORMATS: - try: - return datetime.strptime(timestamp, fmt) - except ValueError: - continue - - # If no format matched, raise error - raise ValueError( - f"Could not parse timestamp string: {timestamp}. " - f"Tried formats: {ALTERNATIVE_FORMATS}" - ) - - else: - raise TypeError( - f"timestamp must be datetime, str, int, or float, got: {type(timestamp)}" - ) - - -def validate_timestamp_format(timestamp_str: str) -> bool: - """ - Validate that a timestamp string matches the standard format. - - Parameters - ---------- - timestamp_str : str - Timestamp string to validate - - Returns - ------- - bool - True if string matches standard format - """ - try: - datetime.strptime(timestamp_str, STANDARD_FORMAT) - return True - except (ValueError, TypeError): - return False - - -def format_for_filename(timestamp: Union[datetime, str]) -> str: - """ - Format timestamp for use in filenames (no spaces or colons). - - Parameters - ---------- - timestamp : datetime or str - Timestamp to format - - Returns - ------- - str - Filename-safe timestamp string (YYYYMMDD_HHMMSS) - - Examples - -------- - >>> dt = datetime(2010, 6, 18, 10, 15, 0) - >>> format_for_filename(dt) - "20100618_101500" - """ - dt = to_datetime(timestamp) - return dt.strftime("%Y%m%d_%H%M%S") - - -def format_for_display(timestamp: Union[datetime, str]) -> str: - """ - Format timestamp for human-readable display. - - Parameters - ---------- - timestamp : datetime or str - Timestamp to format - - Returns - ------- - str - Human-readable timestamp string - - Examples - -------- - >>> dt = datetime(2010, 6, 18, 10, 15, 0) - >>> format_for_display(dt) - "2010-06-18 10:15:00" - """ - dt = to_datetime(timestamp) - return dt.strftime("%Y-%m-%d %H:%M:%S") - - -def parse_patient_recording_start_format( - patient_recording_start_str: str, -) -> datetime: - """ - Parse recording start time from CONFIG.PATIENTS.REC_START format. - - Parameters - ---------- - patient_recording_start_str : str - Recording start time string in format "DD/MM/YYYY, HH:MM:SS" - - Returns - ------- - datetime - Parsed datetime object - - Examples - -------- - >>> parse_patient_recording_start_format("10/06/2010, 07:40:34") - datetime(2010, 6, 10, 7, 40, 34) - """ - REC_START_FORMAT = "%d/%m/%Y, %H:%M:%S" - return datetime.strptime(patient_recording_start_str, REC_START_FORMAT) - - -def get_time_delta_seconds( - start: Union[datetime, str], end: Union[datetime, str] -) -> float: - """ - Calculate time difference in seconds between two timestamps. - - Parameters - ---------- - start : datetime or str - Start timestamp - end : datetime or str - End timestamp - - Returns - ------- - float - Time difference in seconds - """ - start_dt = to_datetime(start) - end_dt = to_datetime(end) - delta = end_dt - start_dt - return delta.total_seconds() - - -def main(args): - """Test timestamp standardization with various inputs.""" - - print("Testing timestamp standardization:") - print("=" * 60) - print(f"Standard format: {STANDARD_FORMAT}") - print() - - # Test data - dt = datetime(2010, 6, 18, 10, 15, 3, 123456) - unix_ts = dt.timestamp() - - test_cases = [ - (dt, "datetime object"), - (unix_ts, "Unix timestamp"), - ("2010-06-18 10:15:03.123456", "Standard format string"), - ("2010-06-18 10:15:03", "Without microseconds"), - ("2010/06/18 10:15:03", "Alternative format 1"), - ("18/06/2010 10:15:03", "Alternative format 2"), - ("10/06/2010, 07:40:34", "REC_START format"), - ("2010-06-18 10:15:03.123456789", "Nanosecond precision"), - ] - - for input_val, description in test_cases: - try: - standardized = normalize_timestamp( - input_val, return_as="str", normalize_utc=False - ) - print(f"✓ {description:30} -> {standardized}") - except Exception as e: - print(f"✗ {description:30} -> ERROR: {e}") - - print("\nDifferent return formats test:") - print("-" * 40) - test_dt = datetime(2010, 6, 18, 10, 15, 3, 123456) - print(f"Input: {test_dt}") - print( - f" as str: {normalize_timestamp(test_dt, return_as='str', normalize_utc=False)}" - ) - print( - f" as datetime: {normalize_timestamp(test_dt, return_as='datetime', normalize_utc=False)}" - ) - print( - f" as timestamp: {normalize_timestamp(test_dt, return_as='timestamp', normalize_utc=False)}" - ) - - print("\nFormat validation tests:") - print("-" * 40) - - valid_tests = [ - ("2010-06-18 10:15:03.123456", True), - ("2010-06-18 10:15:03", False), - ("2010/06/18 10:15:03.123456", False), - ("invalid", False), - ] - - for test_str, expected in valid_tests: - is_valid = validate_timestamp_format(test_str) - status = "✓" if is_valid == expected else "✗" - print( - f"{status} '{test_str[:30]:30}' -> Valid: {is_valid} (expected: {expected})" - ) - - print("\nFilename formatting test:") - print("-" * 40) - filename_ts = format_for_filename(dt) - print(f"Filename format: {filename_ts}") - - print("\nDisplay formatting test:") - print("-" * 40) - display_ts = format_for_display(dt) - print(f"Display format: {display_ts}") - - -def parse_args() -> argparse.Namespace: - """Parse command line arguments.""" - import scitex as stx - - parser = argparse.ArgumentParser( - description="Patient ID standardization utilities for NeuroVista project" - ) - args = parser.parse_args() - stx.str.printc(args, c="yellow") - return args - - -def run_main() -> None: - """Initialize scitex framework, run main function, and cleanup.""" - global CONFIG, CC, sys, plt, rng - - import sys - - import matplotlib.pyplot as plt - - import scitex as stx - - args = parse_args() - - CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start( - sys, - plt, - args=args, - file=__FILE__, - sdir_suffix=None, - verbose=False, - agg=True, - ) - - exit_status = main(args) - - stx.session.close( - CONFIG, - verbose=False, - notify=False, - message="", - exit_status=exit_status, - ) - - -if __name__ == "__main__": - run_main() - -# EOF diff --git a/src/scitex/dt/_skills/SKILL.md b/src/scitex/dt/_skills/SKILL.md deleted file mode 100644 index c90a38ab9..000000000 --- a/src/scitex/dt/_skills/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: stx.dt -description: Short alias for stx.datetime — all the same datetime utilities under a more concise name. ---- - -# stx.dt - -`stx.dt` is an exact alias for `stx.datetime`. Both modules export the same functions and constants. The shorter name is for convenience. - -## Sub-skills - -- [dt-alias.md](dt-alias.md) — Complete function reference with examples, choosing between `stx.dt` and `stx.datetime` - -## Quick Reference - -```python -import scitex as stx -import datetime - -# Linearly spaced datetimes -start = datetime.datetime(2024, 1, 1) -end = datetime.datetime(2024, 1, 2) -times = stx.dt.linspace(start, end, sampling_rate=1000.0) - -# Parse any timestamp -dt = stx.dt.to_datetime("2024-01-15T10:30:00") -s = stx.dt.normalize_timestamp(dt, return_as="str") - -# Format for filenames -fname = stx.dt.format_for_filename(dt) # "20240115_103000" - -# Time delta -delta = stx.dt.get_time_delta_seconds(start, end) - -# Validate -stx.dt.validate_timestamp_format("2024-01-15 10:30:00") # True -``` - -For detailed function documentation see `stx.datetime/_skills/`: -- linspace: `stx.datetime/_skills/linspace.md` -- Timestamp normalization: `stx.datetime/_skills/timestamp-normalization.md` diff --git a/src/scitex/dt/_skills/alias.md b/src/scitex/dt/_skills/alias.md deleted file mode 100644 index 1c8af0055..000000000 --- a/src/scitex/dt/_skills/alias.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -description: stx.dt is a shorter alias for stx.datetime. All exported names are identical. ---- - -# stx.dt Alias - -`stx.dt` and `stx.datetime` export exactly the same names: - -| `stx.dt.*` | `stx.datetime.*` | -|-----------|-----------------| -| `linspace` | `linspace` | -| `normalize_timestamp` | `normalize_timestamp` | -| `to_datetime` | `to_datetime` | -| `format_for_filename` | `format_for_filename` | -| `format_for_display` | `format_for_display` | -| `validate_timestamp_format` | `validate_timestamp_format` | -| `get_time_delta_seconds` | `get_time_delta_seconds` | -| `parse_patient_recording_start_format` | `parse_patient_recording_start_format` | -| `STANDARD_FORMAT` | `STANDARD_FORMAT` | -| `ALTERNATIVE_FORMATS` | `ALTERNATIVE_FORMATS` | - -```python -import scitex as stx - -# These are equivalent -stx.dt.linspace("2026-01-01", "2026-12-31", n=12) -stx.datetime.linspace("2026-01-01", "2026-12-31", n=12) -``` - -For full documentation see the `datetime` skill. diff --git a/src/scitex/dt/_skills/dt-alias.md b/src/scitex/dt/_skills/dt-alias.md deleted file mode 100644 index 2f4060091..000000000 --- a/src/scitex/dt/_skills/dt-alias.md +++ /dev/null @@ -1,61 +0,0 @@ -# stx.dt — Short Alias for stx.datetime - -`stx.dt` is an exact alias for `stx.datetime`. All functions, constants, and behaviors are identical. The shorter name is provided for convenience in interactive sessions. - -## Available Functions - -All functions from `stx.datetime` are available under `stx.dt`: - -```python -import scitex as stx -import datetime - -# Linearly spaced datetime array -start = datetime.datetime(2024, 1, 1) -end = datetime.datetime(2024, 1, 2) - -times = stx.dt.linspace(start, end, n_samples=1000) -times = stx.dt.linspace(start, end, sampling_rate=1000.0) - -# Parse any timestamp format to datetime -dt = stx.dt.to_datetime("2024-01-15T10:30:00") -dt = stx.dt.to_datetime(1705312200) # Unix timestamp - -# Normalize to STANDARD_FORMAT -s = stx.dt.normalize_timestamp(dt, return_as="str", normalize_utc=False) -# "2024-01-15 10:30:00" - -# Format for filenames (no spaces or colons) -fname = stx.dt.format_for_filename(dt) # "20240115_103000" - -# Format for display -display = stx.dt.format_for_display(dt) # "2024-01-15 10:30:00" - -# Compute time difference -delta = stx.dt.get_time_delta_seconds(start, end) # 86400.0 - -# Validate format -stx.dt.validate_timestamp_format("2024-01-15 10:30:00") # True - -# Clinical recording format parser -dt = stx.dt.parse_patient_recording_start_format("10/06/2010, 07:40:34") -``` - -## Constants - -```python -stx.dt.STANDARD_FORMAT # "%Y-%m-%d %H:%M:%S" -stx.dt.ALTERNATIVE_FORMATS # list of 16 formats tried when parsing -``` - -## Choosing Between stx.dt and stx.datetime - -Both are fully interchangeable. Use `stx.dt` for brevity in scripts and notebooks. Use `stx.datetime` when code clarity is more important than conciseness (e.g., in library code that will be read by others). - -```python -# These are equivalent -stx.dt.to_datetime("2024-01-15") -stx.datetime.to_datetime("2024-01-15") -``` - -For detailed documentation of each function, see the sub-skills in `stx.datetime/_skills/`.