From 64f3172e56e9ed1bcedcb5afceb7034fa3ee788f Mon Sep 17 00:00:00 2001
From: ygao <ygao355@wisc.edu>
Date: Tue, 18 Mar 2025 20:19:53 +0000
Subject: [PATCH] a runable python library version

---
 assets/python/data.py             |  80 +++---
 assets/python/latency_viewer.log  | 398 ++++++++++++++++++++++++++++++
 assets/python/metadata.py         |   2 +-
 assets/python/sat_db_functions.py | 230 ++++++++++-------
 assets/python/satellites.py       |   2 +-
 assets/python/test.py             | 188 ++++++++++++++
 assets/python/test_log            |  90 +++++++
 7 files changed, 858 insertions(+), 132 deletions(-)
 create mode 100644 assets/python/latency_viewer.log
 create mode 100755 assets/python/test.py
 create mode 100644 assets/python/test_log

diff --git a/assets/python/data.py b/assets/python/data.py
index 58d3843..652954a 100755
--- a/assets/python/data.py
+++ b/assets/python/data.py
@@ -1,4 +1,4 @@
-#!/home/ygao/latency/venv/bin/python
+#!/home/ygao/miniconda/envs/py39env/bin/python
 import cgi
 import cgitb
 import json
@@ -7,13 +7,12 @@ import os
 import logging
 import pandas as pd
 import traceback
-import subprocess
 
 # Enable detailed CGI error reporting
 cgitb.enable()
 
 # Set up logging - use absolute path to ensure writability
-LOG_DIR = ""
+LOG_DIR = "./"  # Using /tmp which should be writable
 os.makedirs(LOG_DIR, exist_ok=True)
 LOG_FILE = os.path.join(LOG_DIR, "latency_viewer.log")
 
@@ -33,54 +32,61 @@ logger.info(f"Script path: {os.path.abspath(__file__)}")
 logger.info(f"Python version: {sys.version}")
 logger.info(f"User running script: {os.getenv('USER') or 'Unknown'}")
 
+# Import functions from our shared module
+# Define fallback functions in case import fails
+def fallback_get_canonical_id(satellite_id):
+    """Fallback function if import fails - returns the input as-is"""
+    logger.warning(f"Using fallback get_canonical_id for {satellite_id}")
+    return satellite_id
+
+def fallback_get_all_variants(canonical_id):
+    """Fallback function if import fails - returns the canonical ID in a list"""
+    logger.warning(f"Using fallback get_all_variants for {canonical_id}")
+    return [canonical_id]
+
+def fallback_run_sat_latency_query(start_time, end_time, filters=None):
+    """Fallback function if import fails - returns empty list"""
+    logger.error("Using fallback run_sat_latency_query - no data will be returned")
+    return []
+
+# Set default functions to fallbacks
+get_canonical_id = fallback_get_canonical_id
+get_all_variants = fallback_get_all_variants
+run_sat_latency_query = fallback_run_sat_latency_query
+
+# Try to import the real functions
 try:
-    # Log system information
-    logger.info("System information:")
-    whoami_output = subprocess.check_output(["whoami"], stderr=subprocess.STDOUT).decode().strip()
-    logger.info(f"Current user from whoami: {whoami_output}")
-    
-    # Check script permissions
-    logger.info(f"Script permissions: {oct(os.stat(__file__).st_mode)}")
+    # Add possible module locations to the path
+    current_dir = os.path.dirname(os.path.abspath(__file__))
+    sys.path.append(current_dir)
     
-    # Check if we can write to tmp directory
-    tmp_test_path = "/tmp/data_py_test.txt"
-    try:
-        with open(tmp_test_path, 'w') as f:
-            f.write("Test")
-        os.remove(tmp_test_path)
-        logger.info("Successfully wrote to and removed test file in /tmp")
-    except Exception as e:
-        logger.error(f"Failed to write to /tmp: {str(e)}")
+    # Also try parent directory
+    parent_dir = os.path.dirname(current_dir)
+    sys.path.append(parent_dir)
     
-    # Check if sudo is available
-    try:
-        sudo_test = subprocess.run(["sudo", "-l"], capture_output=True, text=True)
-        logger.info(f"Sudo permissions: {sudo_test.stdout}")
-        if sudo_test.returncode != 0:
-            logger.error(f"Sudo test failed: {sudo_test.stderr}")
-    except Exception as e:
-        logger.error(f"Error checking sudo permissions: {str(e)}")
-except Exception as e:
-    logger.error(f"Error during environment checks: {str(e)}")
-
-# Import functions from our shared module
-# Adjust the path as needed to find the module
-try:
-    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
-    logger.info(f"Looking for sat_db_functions in: {os.path.dirname(os.path.abspath(__file__))}")
+    logger.info(f"Looking for sat_db_functions in: {current_dir} and {parent_dir}")
     
     # List directory contents to verify module existence
-    dir_contents = os.listdir(os.path.dirname(os.path.abspath(__file__)))
+    dir_contents = os.listdir(current_dir)
     logger.info(f"Directory contents: {dir_contents}")
     
-    from sat_db_functions import run_sat_latency_query, get_canonical_id, get_all_variants
+    # Try to import the module
+    import sat_db_functions
+    
+    # If successful, override the fallback functions
+    get_canonical_id = sat_db_functions.get_canonical_id
+    get_all_variants = sat_db_functions.get_all_variants
+    run_sat_latency_query = sat_db_functions.run_sat_latency_query
+    
     logger.info("Successfully imported from sat_db_functions")
 except ImportError as e:
     logger.error(f"Import error: {str(e)}")
     logger.error(traceback.format_exc())
+    logger.error("Will use fallback functions that provide limited functionality")
 except Exception as e:
     logger.error(f"Unexpected error during import: {str(e)}")
     logger.error(traceback.format_exc())
+    logger.error("Will use fallback functions that provide limited functionality")
 
 def data_endpoint():
     """
diff --git a/assets/python/latency_viewer.log b/assets/python/latency_viewer.log
new file mode 100644
index 0000000..eb5f4fe
--- /dev/null
+++ b/assets/python/latency_viewer.log
@@ -0,0 +1,398 @@
+2025-03-18 20:17:15,712 - INFO - ================================================================================
+2025-03-18 20:17:15,712 - INFO - Data.py script starting
+2025-03-18 20:17:15,712 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:17:15,712 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:17:15,712 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:17:15,712 - INFO - User running script: Unknown
+2025-03-18 20:17:15,712 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:17:15,712 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:17:15,811 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:17:15,811 - INFO - Calling data_endpoint function...
+2025-03-18 20:17:15,811 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:17:15,811 - INFO - Filter parameters: satellite_id=4B, coverage=None, instrument=GIIRS
+2025-03-18 20:17:15,811 - INFO - Getting canonical form for: 4B
+2025-03-18 20:17:15,811 - INFO - Canonical ID: 4B
+2025-03-18 20:17:15,811 - INFO - All variants: ['4B']
+2025-03-18 20:17:15,811 - INFO - Expanded satellite ID 4B to variants: ['4B']
+2025-03-18 20:17:15,811 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['4B'], 'instrument': 'GIIRS'}
+2025-03-18 20:17:15,811 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:17:15,811 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:17:15,811 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:17:15,811 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:17:15,811 - INFO - Filters: satellite_ids=['4B'], coverage=None, instrument=['GIIRS']
+2025-03-18 20:17:16,161 - INFO - Successfully converted data: 2928 records found
+2025-03-18 20:17:16,163 - INFO - Query returned: 2928 records
+2025-03-18 20:17:16,163 - INFO - Converting to DataFrame...
+2025-03-18 20:17:16,167 - INFO - DataFrame created with shape: (2928, 12)
+2025-03-18 20:17:16,167 - INFO - Processing DataFrame...
+2025-03-18 20:17:16,168 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:17:16,168 - INFO - Cleaning latency data...
+2025-03-18 20:17:16,170 - INFO - DataFrame shape after cleaning: (2928, 12)
+2025-03-18 20:17:16,173 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:17:16,175 - INFO - Converting timestamps...
+2025-03-18 20:17:16,193 - INFO - Converting to records...
+2025-03-18 20:17:16,221 - INFO - Created 2928 result records
+2025-03-18 20:17:16,223 - INFO - Returning response with status code 200 and 2928 records
+2025-03-18 20:17:22,407 - INFO - ================================================================================
+2025-03-18 20:17:22,407 - INFO - Data.py script starting
+2025-03-18 20:17:22,407 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:17:22,407 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:17:22,407 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:17:22,408 - INFO - User running script: Unknown
+2025-03-18 20:17:22,408 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:17:22,408 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:17:22,501 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:17:22,501 - INFO - Calling data_endpoint function...
+2025-03-18 20:17:22,501 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:17:22,501 - INFO - Filter parameters: satellite_id=4B, coverage=None, instrument=GIIRS
+2025-03-18 20:17:22,501 - INFO - Getting canonical form for: 4B
+2025-03-18 20:17:22,501 - INFO - Canonical ID: 4B
+2025-03-18 20:17:22,501 - INFO - All variants: ['4B']
+2025-03-18 20:17:22,502 - INFO - Expanded satellite ID 4B to variants: ['4B']
+2025-03-18 20:17:22,502 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['4B'], 'instrument': 'GIIRS'}
+2025-03-18 20:17:22,502 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:17:22,502 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:17:22,502 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:17:22,502 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:17:22,502 - INFO - Filters: satellite_ids=['4B'], coverage=None, instrument=['GIIRS']
+2025-03-18 20:17:22,808 - INFO - Successfully converted data: 2928 records found
+2025-03-18 20:17:22,810 - INFO - Query returned: 2928 records
+2025-03-18 20:17:22,810 - INFO - Converting to DataFrame...
+2025-03-18 20:17:22,814 - INFO - DataFrame created with shape: (2928, 12)
+2025-03-18 20:17:22,814 - INFO - Processing DataFrame...
+2025-03-18 20:17:22,814 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:17:22,814 - INFO - Cleaning latency data...
+2025-03-18 20:17:22,817 - INFO - DataFrame shape after cleaning: (2928, 12)
+2025-03-18 20:17:22,820 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:17:22,821 - INFO - Converting timestamps...
+2025-03-18 20:17:22,838 - INFO - Converting to records...
+2025-03-18 20:17:22,864 - INFO - Created 2928 result records
+2025-03-18 20:17:22,865 - INFO - Returning response with status code 200 and 2928 records
+2025-03-18 20:17:29,233 - INFO - ================================================================================
+2025-03-18 20:17:29,233 - INFO - Data.py script starting
+2025-03-18 20:17:29,233 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:17:29,233 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:17:29,233 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:17:29,233 - INFO - User running script: Unknown
+2025-03-18 20:17:29,233 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:17:29,233 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:17:29,319 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:17:29,319 - INFO - Calling data_endpoint function...
+2025-03-18 20:17:29,319 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:17:29,319 - INFO - Filter parameters: satellite_id=G16, coverage=None, instrument=ABI
+2025-03-18 20:17:29,319 - INFO - Getting canonical form for: G16
+2025-03-18 20:17:29,319 - INFO - Canonical ID: G16
+2025-03-18 20:17:29,319 - INFO - All variants: ['G16', 'g16']
+2025-03-18 20:17:29,319 - INFO - Expanded satellite ID G16 to variants: ['G16', 'g16']
+2025-03-18 20:17:29,319 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['G16', 'g16'], 'instrument': 'ABI'}
+2025-03-18 20:17:29,319 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:17:29,319 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:17:29,319 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:17:29,320 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:17:29,320 - INFO - Filters: satellite_ids=['G16', 'g16'], coverage=None, instrument=['ABI']
+2025-03-18 20:17:34,602 - INFO - Successfully converted data: 277387 records found
+2025-03-18 20:17:34,679 - INFO - Query returned: 277387 records
+2025-03-18 20:17:34,679 - INFO - Converting to DataFrame...
+2025-03-18 20:17:35,070 - INFO - DataFrame created with shape: (277387, 12)
+2025-03-18 20:17:35,070 - INFO - Processing DataFrame...
+2025-03-18 20:17:35,071 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:17:35,071 - INFO - Cleaning latency data...
+2025-03-18 20:17:35,154 - INFO - DataFrame shape after cleaning: (277387, 12)
+2025-03-18 20:17:35,340 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:17:35,403 - INFO - Converting timestamps...
+2025-03-18 20:17:35,444 - ERROR - Error during data processing: time data "2025-03-18T00:01:16+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 15. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+2025-03-18 20:17:35,445 - ERROR - Traceback (most recent call last):
+  File "/var/www/html/web_internal/students/ygao/latency/assets/python/data.py", line 198, in data_endpoint
+    df['start_time'] = pd.to_datetime(df['start_time']).astype(str)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 1063, in to_datetime
+    cache_array = _maybe_cache(arg, format, cache, convert_listlike)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 247, in _maybe_cache
+    cache_dates = convert_listlike(unique_dates, format)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 433, in _convert_listlike_datetimes
+    return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 467, in _array_strptime_with_fallback
+    result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
+  File "strptime.pyx", line 501, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 451, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 583, in pandas._libs.tslibs.strptime._parse_with_format
+ValueError: time data "2025-03-18T00:01:16+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 15. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+
+2025-03-18 20:17:35,615 - WARNING - Returning error with status code 500: {'message': 'Data processing error: time data "2025-03-18T00:01:16+00:00" doesn\'t match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 15. You might want to try:\n    - passing `format` if your strings have a consistent format;\n    - passing `format=\'ISO8601\'` if your strings are all ISO8601 but not necessarily in exactly the same format;\n    - passing `format=\'mixed\'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.', 'data': []}
+2025-03-18 20:17:35,615 - INFO - Returning response with status code 500 and 0 records
+2025-03-18 20:17:42,583 - INFO - ================================================================================
+2025-03-18 20:17:42,583 - INFO - Data.py script starting
+2025-03-18 20:17:42,583 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:17:42,583 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:17:42,583 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:17:42,583 - INFO - User running script: Unknown
+2025-03-18 20:17:42,583 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:17:42,583 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:17:42,678 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:17:42,678 - INFO - Calling data_endpoint function...
+2025-03-18 20:17:42,679 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:17:42,679 - INFO - Filter parameters: satellite_id=G16, coverage=CONUS, instrument=ABI
+2025-03-18 20:17:42,679 - INFO - Getting canonical form for: G16
+2025-03-18 20:17:42,679 - INFO - Canonical ID: G16
+2025-03-18 20:17:42,679 - INFO - All variants: ['G16', 'g16']
+2025-03-18 20:17:42,679 - INFO - Expanded satellite ID G16 to variants: ['G16', 'g16']
+2025-03-18 20:17:42,679 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['G16', 'g16'], 'coverage': 'CONUS', 'instrument': 'ABI'}
+2025-03-18 20:17:42,679 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:17:42,679 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:17:42,679 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:17:42,679 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:17:42,679 - INFO - Filters: satellite_ids=['G16', 'g16'], coverage=['CONUS'], instrument=['ABI']
+2025-03-18 20:17:43,451 - INFO - Successfully converted data: 25481 records found
+2025-03-18 20:17:43,462 - INFO - Query returned: 25481 records
+2025-03-18 20:17:43,462 - INFO - Converting to DataFrame...
+2025-03-18 20:17:43,500 - INFO - DataFrame created with shape: (25481, 12)
+2025-03-18 20:17:43,501 - INFO - Processing DataFrame...
+2025-03-18 20:17:43,501 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:17:43,501 - INFO - Cleaning latency data...
+2025-03-18 20:17:43,509 - INFO - DataFrame shape after cleaning: (25481, 12)
+2025-03-18 20:17:43,523 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:17:43,530 - INFO - Converting timestamps...
+2025-03-18 20:17:43,536 - ERROR - Error during data processing: time data "2025-03-18T00:01:16+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 1. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+2025-03-18 20:17:43,537 - ERROR - Traceback (most recent call last):
+  File "/var/www/html/web_internal/students/ygao/latency/assets/python/data.py", line 198, in data_endpoint
+    df['start_time'] = pd.to_datetime(df['start_time']).astype(str)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 1063, in to_datetime
+    cache_array = _maybe_cache(arg, format, cache, convert_listlike)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 247, in _maybe_cache
+    cache_dates = convert_listlike(unique_dates, format)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 433, in _convert_listlike_datetimes
+    return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 467, in _array_strptime_with_fallback
+    result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
+  File "strptime.pyx", line 501, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 451, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 583, in pandas._libs.tslibs.strptime._parse_with_format
+ValueError: time data "2025-03-18T00:01:16+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 1. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+
+2025-03-18 20:17:43,549 - WARNING - Returning error with status code 500: {'message': 'Data processing error: time data "2025-03-18T00:01:16+00:00" doesn\'t match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 1. You might want to try:\n    - passing `format` if your strings have a consistent format;\n    - passing `format=\'ISO8601\'` if your strings are all ISO8601 but not necessarily in exactly the same format;\n    - passing `format=\'mixed\'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.', 'data': []}
+2025-03-18 20:17:43,550 - INFO - Returning response with status code 500 and 0 records
+2025-03-18 20:17:47,810 - INFO - ================================================================================
+2025-03-18 20:17:47,810 - INFO - Data.py script starting
+2025-03-18 20:17:47,810 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:17:47,810 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:17:47,810 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:17:47,810 - INFO - User running script: Unknown
+2025-03-18 20:17:47,810 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:17:47,810 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:17:47,901 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:17:47,901 - INFO - Calling data_endpoint function...
+2025-03-18 20:17:47,901 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:17:47,901 - INFO - Filter parameters: satellite_id=G16, coverage=Full Disk, instrument=ABI
+2025-03-18 20:17:47,901 - INFO - Getting canonical form for: G16
+2025-03-18 20:17:47,902 - INFO - Canonical ID: G16
+2025-03-18 20:17:47,902 - INFO - All variants: ['G16', 'g16']
+2025-03-18 20:17:47,902 - INFO - Expanded satellite ID G16 to variants: ['G16', 'g16']
+2025-03-18 20:17:47,902 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['G16', 'g16'], 'coverage': 'Full Disk', 'instrument': 'ABI'}
+2025-03-18 20:17:47,902 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:17:47,902 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:17:47,902 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:17:47,902 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:17:47,902 - INFO - Filters: satellite_ids=['G16', 'g16'], coverage=['Full Disk'], instrument=['ABI']
+2025-03-18 20:17:48,439 - INFO - Successfully converted data: 13027 records found
+2025-03-18 20:17:48,445 - INFO - Query returned: 13027 records
+2025-03-18 20:17:48,445 - INFO - Converting to DataFrame...
+2025-03-18 20:17:48,461 - INFO - DataFrame created with shape: (13027, 12)
+2025-03-18 20:17:48,461 - INFO - Processing DataFrame...
+2025-03-18 20:17:48,461 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:17:48,462 - INFO - Cleaning latency data...
+2025-03-18 20:17:48,465 - INFO - DataFrame shape after cleaning: (13027, 12)
+2025-03-18 20:17:48,472 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:17:48,476 - INFO - Converting timestamps...
+2025-03-18 20:17:48,480 - ERROR - Error during data processing: time data "2025-03-18T00:20:20+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 8. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+2025-03-18 20:17:48,480 - ERROR - Traceback (most recent call last):
+  File "/var/www/html/web_internal/students/ygao/latency/assets/python/data.py", line 198, in data_endpoint
+    df['start_time'] = pd.to_datetime(df['start_time']).astype(str)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 1063, in to_datetime
+    cache_array = _maybe_cache(arg, format, cache, convert_listlike)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 247, in _maybe_cache
+    cache_dates = convert_listlike(unique_dates, format)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 433, in _convert_listlike_datetimes
+    return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 467, in _array_strptime_with_fallback
+    result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
+  File "strptime.pyx", line 501, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 451, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 583, in pandas._libs.tslibs.strptime._parse_with_format
+ValueError: time data "2025-03-18T00:20:20+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 8. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+
+2025-03-18 20:17:48,486 - WARNING - Returning error with status code 500: {'message': 'Data processing error: time data "2025-03-18T00:20:20+00:00" doesn\'t match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 8. You might want to try:\n    - passing `format` if your strings have a consistent format;\n    - passing `format=\'ISO8601\'` if your strings are all ISO8601 but not necessarily in exactly the same format;\n    - passing `format=\'mixed\'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.', 'data': []}
+2025-03-18 20:17:48,486 - INFO - Returning response with status code 500 and 0 records
+2025-03-18 20:17:51,246 - INFO - ================================================================================
+2025-03-18 20:17:51,246 - INFO - Data.py script starting
+2025-03-18 20:17:51,246 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:17:51,246 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:17:51,246 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:17:51,246 - INFO - User running script: Unknown
+2025-03-18 20:17:51,246 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:17:51,246 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:17:51,341 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:17:51,341 - INFO - Calling data_endpoint function...
+2025-03-18 20:17:51,342 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:17:51,342 - INFO - Filter parameters: satellite_id=G18, coverage=Full Disk, instrument=ABI
+2025-03-18 20:17:51,342 - INFO - Getting canonical form for: G18
+2025-03-18 20:17:51,342 - INFO - Canonical ID: G18
+2025-03-18 20:17:51,342 - INFO - All variants: ['G18', 'g18']
+2025-03-18 20:17:51,342 - INFO - Expanded satellite ID G18 to variants: ['G18', 'g18']
+2025-03-18 20:17:51,342 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['G18', 'g18'], 'coverage': 'Full Disk', 'instrument': 'ABI'}
+2025-03-18 20:17:51,342 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:17:51,342 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:17:51,342 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:17:51,342 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:17:51,342 - INFO - Filters: satellite_ids=['G18', 'g18'], coverage=['Full Disk'], instrument=['ABI']
+2025-03-18 20:17:51,927 - INFO - Successfully converted data: 12118 records found
+2025-03-18 20:17:51,933 - INFO - Query returned: 12118 records
+2025-03-18 20:17:51,933 - INFO - Converting to DataFrame...
+2025-03-18 20:17:51,948 - INFO - DataFrame created with shape: (12118, 12)
+2025-03-18 20:17:51,948 - INFO - Processing DataFrame...
+2025-03-18 20:17:51,948 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:17:51,948 - INFO - Cleaning latency data...
+2025-03-18 20:17:51,952 - INFO - DataFrame shape after cleaning: (12118, 12)
+2025-03-18 20:17:51,958 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:17:51,962 - INFO - Converting timestamps...
+2025-03-18 20:17:51,966 - ERROR - Error during data processing: time data "2025-03-18T00:00:20+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 87. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+2025-03-18 20:17:51,966 - ERROR - Traceback (most recent call last):
+  File "/var/www/html/web_internal/students/ygao/latency/assets/python/data.py", line 198, in data_endpoint
+    df['start_time'] = pd.to_datetime(df['start_time']).astype(str)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 1063, in to_datetime
+    cache_array = _maybe_cache(arg, format, cache, convert_listlike)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 247, in _maybe_cache
+    cache_dates = convert_listlike(unique_dates, format)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 433, in _convert_listlike_datetimes
+    return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 467, in _array_strptime_with_fallback
+    result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
+  File "strptime.pyx", line 501, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 451, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 583, in pandas._libs.tslibs.strptime._parse_with_format
+ValueError: time data "2025-03-18T00:00:20+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 87. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+
+2025-03-18 20:17:51,972 - WARNING - Returning error with status code 500: {'message': 'Data processing error: time data "2025-03-18T00:00:20+00:00" doesn\'t match format "%Y-%m-%dT%H:%M:%S.%f%z", at position 87. You might want to try:\n    - passing `format` if your strings have a consistent format;\n    - passing `format=\'ISO8601\'` if your strings are all ISO8601 but not necessarily in exactly the same format;\n    - passing `format=\'mixed\'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.', 'data': []}
+2025-03-18 20:17:51,972 - INFO - Returning response with status code 500 and 0 records
+2025-03-18 20:17:59,565 - INFO - ================================================================================
+2025-03-18 20:17:59,565 - INFO - Data.py script starting
+2025-03-18 20:17:59,565 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:17:59,566 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:17:59,566 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:17:59,566 - INFO - User running script: Unknown
+2025-03-18 20:17:59,566 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:17:59,566 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:17:59,661 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:17:59,661 - INFO - Calling data_endpoint function...
+2025-03-18 20:17:59,661 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:17:59,661 - INFO - Filter parameters: satellite_id=G18, coverage=None, instrument=None
+2025-03-18 20:17:59,661 - INFO - Getting canonical form for: G18
+2025-03-18 20:17:59,661 - INFO - Canonical ID: G18
+2025-03-18 20:17:59,661 - INFO - All variants: ['G18', 'g18']
+2025-03-18 20:17:59,661 - INFO - Expanded satellite ID G18 to variants: ['G18', 'g18']
+2025-03-18 20:17:59,661 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['G18', 'g18']}
+2025-03-18 20:17:59,661 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:17:59,661 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:17:59,662 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:17:59,662 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:17:59,662 - INFO - Filters: satellite_ids=['g18', 'G18'], coverage=None, instrument=None
+2025-03-18 20:18:05,808 - INFO - Successfully converted data: 305500 records found
+2025-03-18 20:18:05,884 - INFO - Query returned: 305500 records
+2025-03-18 20:18:05,884 - INFO - Converting to DataFrame...
+2025-03-18 20:18:06,300 - INFO - DataFrame created with shape: (305500, 12)
+2025-03-18 20:18:06,300 - INFO - Processing DataFrame...
+2025-03-18 20:18:06,301 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:18:06,301 - INFO - Cleaning latency data...
+2025-03-18 20:18:06,388 - INFO - DataFrame shape after cleaning: (305500, 12)
+2025-03-18 20:18:06,595 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:18:06,664 - INFO - Converting timestamps...
+2025-03-18 20:18:06,705 - ERROR - Error during data processing: time data "2025-03-18T00:00:06.900000+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S%z", at position 1. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+2025-03-18 20:18:06,706 - ERROR - Traceback (most recent call last):
+  File "/var/www/html/web_internal/students/ygao/latency/assets/python/data.py", line 198, in data_endpoint
+    df['start_time'] = pd.to_datetime(df['start_time']).astype(str)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 1063, in to_datetime
+    cache_array = _maybe_cache(arg, format, cache, convert_listlike)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 247, in _maybe_cache
+    cache_dates = convert_listlike(unique_dates, format)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 433, in _convert_listlike_datetimes
+    return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
+  File "/home/ygao/miniconda/envs/py39env/lib/python3.9/site-packages/pandas/core/tools/datetimes.py", line 467, in _array_strptime_with_fallback
+    result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
+  File "strptime.pyx", line 501, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 451, in pandas._libs.tslibs.strptime.array_strptime
+  File "strptime.pyx", line 583, in pandas._libs.tslibs.strptime._parse_with_format
+ValueError: time data "2025-03-18T00:00:06.900000+00:00" doesn't match format "%Y-%m-%dT%H:%M:%S%z", at position 1. You might want to try:
+    - passing `format` if your strings have a consistent format;
+    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
+    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
+
+2025-03-18 20:18:06,888 - WARNING - Returning error with status code 500: {'message': 'Data processing error: time data "2025-03-18T00:00:06.900000+00:00" doesn\'t match format "%Y-%m-%dT%H:%M:%S%z", at position 1. You might want to try:\n    - passing `format` if your strings have a consistent format;\n    - passing `format=\'ISO8601\'` if your strings are all ISO8601 but not necessarily in exactly the same format;\n    - passing `format=\'mixed\'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.', 'data': []}
+2025-03-18 20:18:06,888 - INFO - Returning response with status code 500 and 0 records
+2025-03-18 20:18:51,337 - INFO - ================================================================================
+2025-03-18 20:18:51,337 - INFO - Data.py script starting
+2025-03-18 20:18:51,337 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:18:51,337 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:18:51,337 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:18:51,337 - INFO - User running script: Unknown
+2025-03-18 20:18:51,337 - INFO - Looking for sat_db_functions in: /var/www/html/web_internal/students/ygao/latency/assets/python and /var/www/html/web_internal/students/ygao/latency/assets
+2025-03-18 20:18:51,337 - INFO - Directory contents: ['data.py', 'metadata.py', 'sat_db_functions.py', 'satellite_relationships.json', 'satellites.py', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log']
+2025-03-18 20:18:51,434 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:18:51,434 - INFO - Calling data_endpoint function...
+2025-03-18 20:18:51,434 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 20:18:51,434 - INFO - Filter parameters: satellite_id=4B, coverage=None, instrument=GIIRS
+2025-03-18 20:18:51,434 - INFO - Getting canonical form for: 4B
+2025-03-18 20:18:51,434 - INFO - Canonical ID: 4B
+2025-03-18 20:18:51,434 - INFO - All variants: ['4B']
+2025-03-18 20:18:51,435 - INFO - Expanded satellite ID 4B to variants: ['4B']
+2025-03-18 20:18:51,435 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['4B'], 'instrument': 'GIIRS'}
+2025-03-18 20:18:51,435 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:18:51,435 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:18:51,435 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:18:51,435 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-18 00:00:00+00:00, end_date=2025-03-18 23:59:59+00:00
+2025-03-18 20:18:51,435 - INFO - Filters: satellite_ids=['4B'], coverage=None, instrument=['GIIRS']
+2025-03-18 20:18:51,794 - INFO - Successfully converted data: 2928 records found
+2025-03-18 20:18:51,795 - INFO - Query returned: 2928 records
+2025-03-18 20:18:51,795 - INFO - Converting to DataFrame...
+2025-03-18 20:18:51,800 - INFO - DataFrame created with shape: (2928, 12)
+2025-03-18 20:18:51,800 - INFO - Processing DataFrame...
+2025-03-18 20:18:51,800 - INFO - Columns after normalization: ['topic', 'band', 'coverage', 'ingest_source', 'instrument', 'satellite_id', 'section', 'reception_time', 'start_time', 'end_time', 'create_time', 'latency']
+2025-03-18 20:18:51,800 - INFO - Cleaning latency data...
+2025-03-18 20:18:51,802 - INFO - DataFrame shape after cleaning: (2928, 12)
+2025-03-18 20:18:51,806 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:18:51,808 - INFO - Converting timestamps...
+2025-03-18 20:18:51,826 - INFO - Converting to records...
+2025-03-18 20:18:51,855 - INFO - Created 2928 result records
+2025-03-18 20:18:51,857 - INFO - Returning response with status code 200 and 2928 records
diff --git a/assets/python/metadata.py b/assets/python/metadata.py
index 9713587..5d0ae54 100755
--- a/assets/python/metadata.py
+++ b/assets/python/metadata.py
@@ -1,4 +1,4 @@
-#!/home/ygao/latency/venv/bin/python
+#!/home/ygao/miniconda/envs/py39env/bin/python
 import cgi
 import json
 import os
diff --git a/assets/python/sat_db_functions.py b/assets/python/sat_db_functions.py
index 2235fb9..f0e8647 100755
--- a/assets/python/sat_db_functions.py
+++ b/assets/python/sat_db_functions.py
@@ -1,15 +1,22 @@
-#!/home/ygao/latency/venv/bin/python
+#!/home/ygao/miniconda/envs/py39env/bin/python
 import os
 import json
-import subprocess
 import logging
 import pandas as pd
+from datetime import datetime, timezone,timedelta
+from sat_latency.interface import satellite_data_from_filters
 
+# Custom JSON encoder to handle datetime objects
+class DateTimeEncoder(json.JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, (datetime, pd.Timestamp)):
+            return obj.isoformat()
+        return super().default(obj)
+                               
 # Set up logging
 logger = logging.getLogger()
 
-# Path to conda environment and database
-CONDA_ENV_PATH = "/home/oper/.mdrexler_conda"
+# Path to database
 SATELLITE_DATA_DIR = "/data/sat_latency"  # Path to your latency database
 RELATIONSHIPS_FILE = "satellite_relationships.json"  # Path to your prebuilt relationships file
 
@@ -154,7 +161,7 @@ def load_relationship_data():
 
 def run_sat_latency_query(start_time, end_time, filters=None):
     """
-    Directly query the satellite latency database using sat_latency_interface
+    Query the satellite latency database using sat_latency.interface package
     
     Args:
         start_time (str): Start time in ISO format (YYYY-MM-DDTHH:MM:SS)
@@ -164,99 +171,136 @@ def run_sat_latency_query(start_time, end_time, filters=None):
     Returns:
         list: List of latency records as dictionaries
     """
-    # Expand satellite IDs to include all variants
-    if filters and "satellite-id" in filters:
-        satellite_id = filters["satellite-id"]
-        
-        # Handle comma-separated list of satellites
-        if isinstance(satellite_id, str) and ',' in satellite_id:
-            satellite_ids = [s.strip() for s in satellite_id.split(',')]
-            expanded_ids = []
-            
-            for sat_id in satellite_ids:
-                # Get the canonical form
-                canonical_id = get_canonical_id(sat_id)
-                
-                # Add all variants of this canonical ID
-                expanded_ids.extend(get_all_variants(canonical_id))
-            
-            # Remove duplicates
-            expanded_ids = list(set(expanded_ids))
-            filters["satellite-id"] = expanded_ids
-        # Handle single satellite ID
-        elif isinstance(satellite_id, str):
-            canonical_id = get_canonical_id(satellite_id)
-            filters["satellite-id"] = get_all_variants(canonical_id)
-    
-    # Build the command exactly as specified
-    base_cmd = "module load miniconda/3.6-base && source activate ~/.mdrexler_conda && sat_latency_interface"
-    
-    # Add database path and time parameters
-    cmd = f"{base_cmd} -d {SATELLITE_DATA_DIR} --from '{start_time}' --until '{end_time}' --output-type json"
-    
-    # Add filters if provided
-    if filters:
-        for key, values in filters.items():
-            if values and (isinstance(values, list) or isinstance(values, str)):
-                if isinstance(values, str):
-                    values = [values]
-                filter_values = " ".join(f'"{v}"' for v in values if v)
-                if filter_values:
-                    cmd += f" --{key} {filter_values}"
-    
-    logger.info(f"Running command: {cmd}")
-    
     try:
-        # Create a temporary shell script to run the command
-        script_path = "/tmp/run_sat_latency.sh"
-        with open(script_path, 'w') as f:
-            f.write("#!/bin/bash\n")
-            f.write(cmd + "\n")
+        logger.info(f"Querying satellite latency data from {start_time} to {end_time}")
         
-        # Make the script executable
-        os.chmod(script_path, 0o755)
-        
-        # Run the script using sudo as the oper user
-        sudo_cmd = ["sudo", "-u", "oper", "-i", script_path]
+        # Convert string ISO timestamps to datetime objects if they are strings
+        if isinstance(start_time, str):
+            start_datetime = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
+        else:
+            start_datetime = start_time
+            
+        if isinstance(end_time, str):
+            end_datetime = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
+        else:
+            end_datetime = end_time
         
-        logger.info(f"Executing: {' '.join(sudo_cmd)}")
+        # Ensure timezone is set
+        if start_datetime.tzinfo is None:
+            start_datetime = start_datetime.replace(tzinfo=timezone.utc)
+        if end_datetime.tzinfo is None:
+            end_datetime = end_datetime.replace(tzinfo=timezone.utc)
+            
+        logger.info(f"Converted timestamps: {start_datetime} to {end_datetime}")
         
-        # Use PIPE for stdout and stderr
-        process = subprocess.Popen(
-            sudo_cmd, 
-            stdout=subprocess.PIPE, 
-            stderr=subprocess.PIPE,
-            universal_newlines=True
+       # Initialize filter parameters for the sat_latency API
+        satellite_ids = None
+        coverage = None
+        instrument = None
+
+        # Process filters
+        if filters:
+            # Expand satellite IDs to include all variants
+            if "satellite-id" in filters:
+                satellite_id = filters["satellite-id"]
+                        
+                # Handle list or comma-separated list of satellites
+                if isinstance(satellite_id, list):
+                    expanded_ids = []
+                    for sat_id in satellite_id:
+                        canonical_id = get_canonical_id(sat_id)
+                        expanded_ids.extend(get_all_variants(canonical_id))
+                    # Remove duplicates
+                    satellite_ids = list(set(expanded_ids))
+                elif isinstance(satellite_id, str) and ',' in satellite_id:
+                    satellite_ids_list = [s.strip() for s in satellite_id.split(',')]
+                    expanded_ids = []
+                    for sat_id in satellite_ids_list:
+                        canonical_id = get_canonical_id(sat_id)
+                        expanded_ids.extend(get_all_variants(canonical_id))
+                    satellite_ids = list(set(expanded_ids))
+                elif isinstance(satellite_id, str):
+                    canonical_id = get_canonical_id(satellite_id)
+                    satellite_ids = get_all_variants(canonical_id)
+                
+            # Get coverage filter
+            if "coverage" in filters:
+                coverage_value = filters["coverage"]
+                # Convert coverage to a list if it's a string
+                if isinstance(coverage_value, str):
+                    if ',' in coverage_value:
+                        coverage = [c.strip() for c in coverage_value.split(',')]
+                    else:
+                        coverage = [coverage_value]
+                else:
+                    coverage = coverage_value  # Already a list or None
+                
+            # Get instrument filter
+            if "instrument" in filters:
+                instrument_value = filters["instrument"]
+                # Convert instrument to a list if it's a string
+                if isinstance(instrument_value, str):
+                    if ',' in instrument_value:
+                        instrument = [i.strip() for i in instrument_value.split(',')]
+                    else:
+                        instrument = [instrument_value]
+                else:
+                    instrument = instrument_value  # Already a list or None
+
+        # Log the query parameters
+        logger.info(f"Query parameters: database={SATELLITE_DATA_DIR}, start_date={start_datetime}, end_date={end_datetime}")
+        logger.info(f"Filters: satellite_ids={satellite_ids}, coverage={coverage}, instrument={instrument}")
+
+        # Call the sat_latency.interface function
+        data = satellite_data_from_filters(
+            SATELLITE_DATA_DIR,
+            start_date=start_datetime,
+            end_date=end_datetime,
+            satellite_ids=satellite_ids,
+            coverages=coverage,
+            instruments=instrument
         )
-        
-        # Get the output and error
-        stdout, stderr = process.communicate()
-        
-        # Check if the command was successful
-        if process.returncode != 0:
-            logger.error(f"Command failed with exit code {process.returncode}: {stderr}")
-            return []
-        
-        # Log the first part of the output
-        if stdout:
-            logger.info(f"Command output (first 200 chars): {stdout[:200]}...")
+        # Convert result to a list of dictionaries for JSON serialization
+        if data is not None:
+            try:
+                # Convert Polars DataFrame to list of dictionaries
+                records = data.to_dicts()
+                
+                # Process datetime objects for JSON serialization
+                processed_records = []
+                for record in records:
+                    processed_record = {}
+                    for key, value in record.items():
+                        if isinstance(value, (datetime, pd.Timestamp)):
+                            processed_record[key] = value.isoformat()
+                        else:
+                            processed_record[key] = value
+                    processed_records.append(processed_record)
+                
+                logger.info(f"Successfully converted data: {len(processed_records)} records found")
+                return processed_records
+                
+            except Exception as e:
+                logger.error(f"Error converting Polars DataFrame to dict: {str(e)}")
+                # Fallback method if to_dicts() is not available
+                try:
+                    pandas_df = data.to_pandas()
+                    
+                    # Convert datetime columns to strings
+                    for col in pandas_df.select_dtypes(include=['datetime64']).columns:
+                        pandas_df[col] = pandas_df[col].astype(str)
+                    
+                    records = pandas_df.to_dict(orient='records')
+                    logger.info(f"Successfully converted data via pandas: {len(records)} records found")
+                    return records
+                except Exception as e2:
+                    logger.error(f"Error in pandas conversion fallback: {str(e2)}")
+                    return []
         else:
-            logger.warning("Command returned empty output")
-            
-        # Parse the JSON output
-        try:
-            data = json.loads(stdout)
-            logger.info(f"Successfully parsed JSON data: {len(data)} records found")
-            return data
-        except json.JSONDecodeError as e:
-            logger.error(f"Failed to parse JSON output: {e}")
-            logger.error(f"Raw output (first 500 chars): {stdout[:500]}...")
+            logger.warning("Query returned None")
             return []
-            
     except Exception as e:
-        logger.error(f"Error executing command: {str(e)}")
-        return []
-    finally:
-        # Clean up temporary script
-        if os.path.exists(script_path):
-            os.remove(script_path)
\ No newline at end of file
+        logger.error(f"Error executing satellite latency query: {str(e)}")
+        import traceback
+        logger.error(traceback.format_exc())
+        return []
\ No newline at end of file
diff --git a/assets/python/satellites.py b/assets/python/satellites.py
index b0b2119..e8bb782 100755
--- a/assets/python/satellites.py
+++ b/assets/python/satellites.py
@@ -1,4 +1,4 @@
-#!/home/ygao/latency/venv/bin/python
+#!/home/ygao/miniconda/envs/py39env/bin/python
 import cgi
 import json
 import sys
diff --git a/assets/python/test.py b/assets/python/test.py
new file mode 100755
index 0000000..e7e329b
--- /dev/null
+++ b/assets/python/test.py
@@ -0,0 +1,188 @@
+#!/home/ygao/miniconda/envs/py39env/bin/python
+"""
+CGI script to test satellite data access with proper JSON serialization
+"""
+
+import cgi
+import cgitb
+import os
+import sys
+import json
+from datetime import datetime, timezone, timedelta
+import traceback
+
+# Enable CGI error reporting to browser
+cgitb.enable()
+
+# Set up basic logging to a specific file
+import logging
+LOG_FILE = "./test_log"
+logging.basicConfig(
+    level=logging.INFO,
+    format='%(asctime)s - %(levelname)s - %(message)s',
+    filename=LOG_FILE,
+    filemode='a'
+)
+logger = logging.getLogger()
+
+# Log script start and environment
+logger.info("=" * 80)
+logger.info("Test CGI script starting")
+logger.info(f"Current working directory: {os.getcwd()}")
+logger.info(f"Script path: {os.path.abspath(__file__)}")
+logger.info(f"Python executable: {sys.executable}")
+logger.info(f"Python version: {sys.version}")
+logger.info(f"User running script: {os.getenv('USER') or 'Unknown'}")
+logger.info(f"Environment variables: {dict(os.environ)}")
+
+def check_imports():
+    """Test importing required modules and log results"""
+    modules_to_check = [
+        'pandas', 
+        'sat_latency.interface'
+    ]
+    
+    results = {}
+    
+    for module_name in modules_to_check:
+        try:
+            logger.info(f"Attempting to import {module_name}...")
+            if module_name == 'pandas':
+                import pandas
+                results[module_name] = f"Success (version: {pandas.__version__})"
+            elif module_name == 'sat_latency.interface':
+                from sat_latency.interface import satellite_data_from_filters
+                results[module_name] = "Success"
+        except Exception as e:
+            logger.error(f"Failed to import {module_name}: {str(e)}")
+            results[module_name] = f"Error: {str(e)}"
+    
+    return results
+
+def check_directory_permissions():
+    """Check if the current user has access to the satellite data directory"""
+    data_dir = "/data/sat_latency"
+    
+    # Get current user info
+    current_user = os.getenv('USER') or "Unknown"
+    try:
+        import pwd
+        uid = os.getuid()
+        current_user = pwd.getpwuid(uid).pw_name
+    except:
+        pass
+    
+    # Check if directory exists
+    dir_exists = os.path.exists(data_dir)
+    logger.info(f"Directory exists: {dir_exists}")
+    
+    # Check if it's readable
+    dir_readable = os.access(data_dir, os.R_OK)
+    logger.info(f"Directory readable: {dir_readable}")
+    
+    # Get directory stats if possible
+    try:
+        dir_stat = os.stat(data_dir)
+        dir_mode = dir_stat.st_mode
+        dir_perms = oct(dir_mode & 0o777)
+        logger.info(f"Directory permissions: {dir_perms}")
+        try:
+            import pwd, grp
+            dir_owner = pwd.getpwuid(dir_stat.st_uid).pw_name
+            dir_group = grp.getgrgid(dir_stat.st_gid).gr_name
+            logger.info(f"Directory owner: {dir_owner}")
+            logger.info(f"Directory group: {dir_group}")
+        except:
+            pass
+    except Exception as e:
+        logger.error(f"Error getting directory stats: {str(e)}")
+    
+    # Try to list directory contents
+    try:
+        dir_contents = os.listdir(data_dir)
+        logger.info(f"Successfully listed directory with {len(dir_contents)} items")
+        logger.info(f"First few items: {dir_contents[:5] if len(dir_contents) > 0 else 'empty'}")
+        return {
+            "success": True,
+            "user": current_user,
+            "exists": dir_exists,
+            "readable": dir_readable,
+            "items_count": len(dir_contents)
+        }
+    except Exception as e:
+        logger.error(f"Failed to list directory contents: {str(e)}")
+        return {
+            "success": False,
+            "user": current_user,
+            "exists": dir_exists,
+            "readable": dir_readable,
+            "error": str(e)
+        }
+
+def run_diagnostics():
+    """Run all diagnostic tests and return results"""
+    results = {
+        "timestamp": datetime.now().isoformat(),
+        "import_checks": None,
+        "directory_permissions": None,
+        "success": False,
+        "error": None
+    }
+    
+    try:
+        # Check imports
+        results["import_checks"] = check_imports()
+        
+        # Check directory permissions
+        results["directory_permissions"] = check_directory_permissions()
+        
+        # Consider test successful if we got this far
+        results["success"] = True
+        
+    except Exception as e:
+        logger.error(f"Error in diagnostics: {str(e)}")
+        logger.error(traceback.format_exc())
+        results["error"] = str(e)
+    
+    return results
+
+# Main CGI handler
+if __name__ == "__main__":
+    try:
+        # Print HTTP headers
+        print("Content-Type: application/json")
+        print("Cache-Control: no-cache")
+        print()  # Empty line after headers
+        
+        # Run diagnostics
+        results = run_diagnostics()
+        
+        # Custom JSON encoder to handle datetime objects
+        class DateTimeEncoder(json.JSONEncoder):
+            def default(self, obj):
+                if isinstance(obj, datetime):
+                    return obj.isoformat()
+                return super().default(obj)
+        
+        # Output JSON response using custom encoder
+        print(json.dumps(results, cls=DateTimeEncoder, indent=2))
+        
+    except Exception as e:
+        # Handle any final errors
+        error_response = {
+            "success": False,
+            "message": f"Critical error: {str(e)}",
+            "error_type": type(e).__name__,
+            "traceback": traceback.format_exc()
+        }
+        
+        # Log the error
+        logger.error(f"Critical error in main block: {str(e)}")
+        logger.error(traceback.format_exc())
+        
+        try:
+            # Try to output JSON response
+            print(json.dumps(error_response))
+        except:
+            # If that fails, output plain text
+            print(f"Critical error: {str(e)}")
\ No newline at end of file
diff --git a/assets/python/test_log b/assets/python/test_log
new file mode 100644
index 0000000..11e4fd8
--- /dev/null
+++ b/assets/python/test_log
@@ -0,0 +1,90 @@
+2025-03-18 20:13:53,713 - INFO - ================================================================================
+2025-03-18 20:13:53,713 - INFO - Test CGI script starting
+2025-03-18 20:13:53,713 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:13:53,713 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/test.py
+2025-03-18 20:13:53,713 - INFO - Python executable: /home/ygao/miniconda/envs/py39env/bin/python
+2025-03-18 20:13:53,713 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:13:53,713 - INFO - User running script: Unknown
+2025-03-18 20:13:53,713 - INFO - Environment variables: {'HTTPS': 'on', 'SSL_TLS_SNI': 'qcweb2.ssec.wisc.edu', 'SSL_SERVER_S_DN_C': 'US', 'SSL_SERVER_S_DN_ST': 'Wisconsin', 'SSL_SERVER_S_DN_O': 'University of Wisconsin-Madison', 'SSL_SERVER_S_DN_CN': 'ssec.wisc.edu', 'SSL_SERVER_I_DN_C': 'US', 'SSL_SERVER_I_DN_O': 'Internet2', 'SSL_SERVER_I_DN_CN': 'InCommon RSA Server CA 2', 'SSL_SERVER_SAN_DNS_0': 'ssec.wisc.edu', 'SSL_SERVER_SAN_DNS_1': '*.ssec.wisc.edu', 'SSL_VERSION_INTERFACE': 'mod_ssl/2.4.37', 'SSL_VERSION_LIBRARY': 'OpenSSL/1.1.1k', 'SSL_PROTOCOL': 'TLSv1.3', 'SSL_SECURE_RENEG': 'false', 'SSL_COMPRESS_METHOD': 'NULL', 'SSL_CIPHER': 'TLS_AES_256_GCM_SHA384', 'SSL_CIPHER_EXPORT': 'false', 'SSL_CIPHER_USEKEYSIZE': '256', 'SSL_CIPHER_ALGKEYSIZE': '256', 'SSL_CLIENT_VERIFY': 'NONE', 'SSL_SERVER_M_VERSION': '3', 'SSL_SERVER_M_SERIAL': '1A94C0029DE6D1DB1ED2F41A9D6CFFAF', 'SSL_SERVER_V_START': 'Jan  3 00:00:00 2025 GMT', 'SSL_SERVER_V_END': 'Jan  3 23:59:59 2026 GMT', 'SSL_SERVER_S_DN': 'CN=ssec.wisc.edu,O=University of Wisconsin-Madison,ST=Wisconsin,C=US', 'SSL_SERVER_I_DN': 'CN=InCommon RSA Server CA 2,O=Internet2,C=US', 'SSL_SERVER_A_KEY': 'rsaEncryption', 'SSL_SERVER_A_SIG': 'sha384WithRSAEncryption', 'SSL_SESSION_ID': '757819207e20b3b71b9727fec9682704c887762bd70eb83fdc1836ff629f41be', 'SSL_SESSION_RESUMED': 'Resumed', 'HTTP_HOST': 'qcweb2.ssec.wisc.edu', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_SEC_CH_UA': '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', 'HTTP_SEC_CH_UA_MOBILE': '?0', 'HTTP_SEC_CH_UA_PLATFORM': '"macOS"', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'HTTP_SEC_FETCH_SITE': 'none', 'HTTP_SEC_FETCH_MODE': 'navigate', 'HTTP_SEC_FETCH_USER': '?1', 'HTTP_SEC_FETCH_DEST': 'document', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br, zstd', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'ELOQUA=GUID=61A25E7C6FDF4120B104E80CDC16BEEB; ELQSTATUS=OK; __gsas=ID=2d8e199b2946bc51:T=1731094010:RT=1731094010:S=ALNI_Ma_5YNiNoq0NlLt72y2RCPc1CEb_g; _hp2_props.3001039959=%7B%22Base.appName%22%3A%22Canvas%22%7D; _hp2_id.3001039959=%7B%22userId%22%3A%225978514486938310%22%2C%22pageviewId%22%3A%22440562338760070%22%2C%22sessionId%22%3A%221338963026909406%22%2C%22identity%22%3A%22uu-2-5ca2d709facfd50d34ed98183225e90857d8ffe9c3053efd43baa7bb47a36596-0oZnoj0EXwmozuvldCGvsOXhduumpWAJplTCVyht%22%2C%22trackerVersion%22%3A%224.0%22%2C%22identityField%22%3Anull%2C%22isIdentified%22%3A1%7D; _ga_7YGT91LS0G=GS1.1.1739309055.3.1.1739309087.0.0.0; _ga_8MJYP9RHDL=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_WV39VETPEH=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_W0KBLYZJ8L=GS1.1.1739548831.2.0.1739548848.0.0.0; _ga_DLGR3GYC5K=GS1.2.1739909437.1.0.1739909437.0.0.0; nmstat=40b68e16-6de2-14f7-2443-7e8c4cc20c1c; _ga=GA1.1.1637011006.1729780911; _ga_TNJJ6N9F4J=GS1.1.1741128288.3.1.1741128312.0.0.0; _clck=10n0iyd%7C2%7Cfu7%7C0%7C1882; _ga_PLJ9E3ZY82=GS1.1.1741966108.6.1.1741966144.0.0.0', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', 'SERVER_SIGNATURE': '', 'SERVER_SOFTWARE': 'Apache', 'SERVER_NAME': 'qcweb2.ssec.wisc.edu', 'SERVER_ADDR': '128.104.111.208', 'SERVER_PORT': '443', 'REMOTE_ADDR': '128.104.110.45', 'DOCUMENT_ROOT': '/var/www/html', 'REQUEST_SCHEME': 'https', 'CONTEXT_PREFIX': '', 'CONTEXT_DOCUMENT_ROOT': '/var/www/html', 'SERVER_ADMIN': '[no address given]', 'SCRIPT_FILENAME': '/var/www/html/web_internal/students/ygao/latency/assets/python/test.py', 'REMOTE_PORT': '54623', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'REQUEST_URI': '/web_internal/students/ygao/latency/assets/python/test.py', 'SCRIPT_NAME': '/web_internal/students/ygao/latency/assets/python/test.py', 'LC_CTYPE': 'C.UTF-8'}
+2025-03-18 20:13:53,713 - INFO - Attempting to import pandas...
+2025-03-18 20:13:54,054 - INFO - Attempting to import sat_latency.interface...
+2025-03-18 20:13:54,146 - INFO - Directory exists: True
+2025-03-18 20:13:54,146 - INFO - Directory readable: True
+2025-03-18 20:13:54,147 - INFO - Directory permissions: 0o755
+2025-03-18 20:13:54,147 - INFO - Directory owner: oper
+2025-03-18 20:13:54,147 - INFO - Directory group: domain users
+2025-03-18 20:13:54,147 - INFO - Successfully listed directory with 3 items
+2025-03-18 20:13:54,147 - INFO - First few items: ['2024', '2000', '2025']
+2025-03-18 20:13:56,358 - INFO - ================================================================================
+2025-03-18 20:13:56,358 - INFO - Test CGI script starting
+2025-03-18 20:13:56,358 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:13:56,358 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/test.py
+2025-03-18 20:13:56,358 - INFO - Python executable: /home/ygao/miniconda/envs/py39env/bin/python
+2025-03-18 20:13:56,358 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:13:56,358 - INFO - User running script: Unknown
+2025-03-18 20:13:56,358 - INFO - Environment variables: {'HTTPS': 'on', 'SSL_TLS_SNI': 'qcweb2.ssec.wisc.edu', 'SSL_SERVER_S_DN_C': 'US', 'SSL_SERVER_S_DN_ST': 'Wisconsin', 'SSL_SERVER_S_DN_O': 'University of Wisconsin-Madison', 'SSL_SERVER_S_DN_CN': 'ssec.wisc.edu', 'SSL_SERVER_I_DN_C': 'US', 'SSL_SERVER_I_DN_O': 'Internet2', 'SSL_SERVER_I_DN_CN': 'InCommon RSA Server CA 2', 'SSL_SERVER_SAN_DNS_0': 'ssec.wisc.edu', 'SSL_SERVER_SAN_DNS_1': '*.ssec.wisc.edu', 'SSL_VERSION_INTERFACE': 'mod_ssl/2.4.37', 'SSL_VERSION_LIBRARY': 'OpenSSL/1.1.1k', 'SSL_PROTOCOL': 'TLSv1.3', 'SSL_SECURE_RENEG': 'false', 'SSL_COMPRESS_METHOD': 'NULL', 'SSL_CIPHER': 'TLS_AES_256_GCM_SHA384', 'SSL_CIPHER_EXPORT': 'false', 'SSL_CIPHER_USEKEYSIZE': '256', 'SSL_CIPHER_ALGKEYSIZE': '256', 'SSL_CLIENT_VERIFY': 'NONE', 'SSL_SERVER_M_VERSION': '3', 'SSL_SERVER_M_SERIAL': '1A94C0029DE6D1DB1ED2F41A9D6CFFAF', 'SSL_SERVER_V_START': 'Jan  3 00:00:00 2025 GMT', 'SSL_SERVER_V_END': 'Jan  3 23:59:59 2026 GMT', 'SSL_SERVER_S_DN': 'CN=ssec.wisc.edu,O=University of Wisconsin-Madison,ST=Wisconsin,C=US', 'SSL_SERVER_I_DN': 'CN=InCommon RSA Server CA 2,O=Internet2,C=US', 'SSL_SERVER_A_KEY': 'rsaEncryption', 'SSL_SERVER_A_SIG': 'sha384WithRSAEncryption', 'SSL_SESSION_ID': '757819207e20b3b71b9727fec9682704c887762bd70eb83fdc1836ff629f41be', 'SSL_SESSION_RESUMED': 'Resumed', 'HTTP_HOST': 'qcweb2.ssec.wisc.edu', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_SEC_CH_UA': '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', 'HTTP_SEC_CH_UA_MOBILE': '?0', 'HTTP_SEC_CH_UA_PLATFORM': '"macOS"', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'HTTP_SEC_FETCH_SITE': 'none', 'HTTP_SEC_FETCH_MODE': 'navigate', 'HTTP_SEC_FETCH_USER': '?1', 'HTTP_SEC_FETCH_DEST': 'document', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br, zstd', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'ELOQUA=GUID=61A25E7C6FDF4120B104E80CDC16BEEB; ELQSTATUS=OK; __gsas=ID=2d8e199b2946bc51:T=1731094010:RT=1731094010:S=ALNI_Ma_5YNiNoq0NlLt72y2RCPc1CEb_g; _hp2_props.3001039959=%7B%22Base.appName%22%3A%22Canvas%22%7D; _hp2_id.3001039959=%7B%22userId%22%3A%225978514486938310%22%2C%22pageviewId%22%3A%22440562338760070%22%2C%22sessionId%22%3A%221338963026909406%22%2C%22identity%22%3A%22uu-2-5ca2d709facfd50d34ed98183225e90857d8ffe9c3053efd43baa7bb47a36596-0oZnoj0EXwmozuvldCGvsOXhduumpWAJplTCVyht%22%2C%22trackerVersion%22%3A%224.0%22%2C%22identityField%22%3Anull%2C%22isIdentified%22%3A1%7D; _ga_7YGT91LS0G=GS1.1.1739309055.3.1.1739309087.0.0.0; _ga_8MJYP9RHDL=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_WV39VETPEH=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_W0KBLYZJ8L=GS1.1.1739548831.2.0.1739548848.0.0.0; _ga_DLGR3GYC5K=GS1.2.1739909437.1.0.1739909437.0.0.0; nmstat=40b68e16-6de2-14f7-2443-7e8c4cc20c1c; _ga=GA1.1.1637011006.1729780911; _ga_TNJJ6N9F4J=GS1.1.1741128288.3.1.1741128312.0.0.0; _clck=10n0iyd%7C2%7Cfu7%7C0%7C1882; _ga_PLJ9E3ZY82=GS1.1.1741966108.6.1.1741966144.0.0.0', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', 'SERVER_SIGNATURE': '', 'SERVER_SOFTWARE': 'Apache', 'SERVER_NAME': 'qcweb2.ssec.wisc.edu', 'SERVER_ADDR': '128.104.111.208', 'SERVER_PORT': '443', 'REMOTE_ADDR': '128.104.110.45', 'DOCUMENT_ROOT': '/var/www/html', 'REQUEST_SCHEME': 'https', 'CONTEXT_PREFIX': '', 'CONTEXT_DOCUMENT_ROOT': '/var/www/html', 'SERVER_ADMIN': '[no address given]', 'SCRIPT_FILENAME': '/var/www/html/web_internal/students/ygao/latency/assets/python/test.py', 'REMOTE_PORT': '54623', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'REQUEST_URI': '/web_internal/students/ygao/latency/assets/python/test.py', 'SCRIPT_NAME': '/web_internal/students/ygao/latency/assets/python/test.py', 'LC_CTYPE': 'C.UTF-8'}
+2025-03-18 20:13:56,358 - INFO - Attempting to import pandas...
+2025-03-18 20:13:56,757 - INFO - Attempting to import sat_latency.interface...
+2025-03-18 20:13:56,848 - INFO - Directory exists: True
+2025-03-18 20:13:56,849 - INFO - Directory readable: True
+2025-03-18 20:13:56,849 - INFO - Directory permissions: 0o755
+2025-03-18 20:13:56,849 - INFO - Directory owner: oper
+2025-03-18 20:13:56,849 - INFO - Directory group: domain users
+2025-03-18 20:13:56,849 - INFO - Successfully listed directory with 3 items
+2025-03-18 20:13:56,849 - INFO - First few items: ['2024', '2000', '2025']
+2025-03-18 20:14:01,723 - INFO - ================================================================================
+2025-03-18 20:14:01,723 - INFO - Test CGI script starting
+2025-03-18 20:14:01,723 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:14:01,723 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/test.py
+2025-03-18 20:14:01,723 - INFO - Python executable: /home/ygao/miniconda/envs/py39env/bin/python
+2025-03-18 20:14:01,723 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:14:01,723 - INFO - User running script: Unknown
+2025-03-18 20:14:01,723 - INFO - Environment variables: {'HTTPS': 'on', 'SSL_TLS_SNI': 'qcweb2.ssec.wisc.edu', 'SSL_SERVER_S_DN_C': 'US', 'SSL_SERVER_S_DN_ST': 'Wisconsin', 'SSL_SERVER_S_DN_O': 'University of Wisconsin-Madison', 'SSL_SERVER_S_DN_CN': 'ssec.wisc.edu', 'SSL_SERVER_I_DN_C': 'US', 'SSL_SERVER_I_DN_O': 'Internet2', 'SSL_SERVER_I_DN_CN': 'InCommon RSA Server CA 2', 'SSL_SERVER_SAN_DNS_0': 'ssec.wisc.edu', 'SSL_SERVER_SAN_DNS_1': '*.ssec.wisc.edu', 'SSL_VERSION_INTERFACE': 'mod_ssl/2.4.37', 'SSL_VERSION_LIBRARY': 'OpenSSL/1.1.1k', 'SSL_PROTOCOL': 'TLSv1.3', 'SSL_SECURE_RENEG': 'false', 'SSL_COMPRESS_METHOD': 'NULL', 'SSL_CIPHER': 'TLS_AES_256_GCM_SHA384', 'SSL_CIPHER_EXPORT': 'false', 'SSL_CIPHER_USEKEYSIZE': '256', 'SSL_CIPHER_ALGKEYSIZE': '256', 'SSL_CLIENT_VERIFY': 'NONE', 'SSL_SERVER_M_VERSION': '3', 'SSL_SERVER_M_SERIAL': '1A94C0029DE6D1DB1ED2F41A9D6CFFAF', 'SSL_SERVER_V_START': 'Jan  3 00:00:00 2025 GMT', 'SSL_SERVER_V_END': 'Jan  3 23:59:59 2026 GMT', 'SSL_SERVER_S_DN': 'CN=ssec.wisc.edu,O=University of Wisconsin-Madison,ST=Wisconsin,C=US', 'SSL_SERVER_I_DN': 'CN=InCommon RSA Server CA 2,O=Internet2,C=US', 'SSL_SERVER_A_KEY': 'rsaEncryption', 'SSL_SERVER_A_SIG': 'sha384WithRSAEncryption', 'SSL_SESSION_ID': '91a403b590ff1c6984a5af90269f086e0ccdf4d5300d06bab032ae86bb7fadff', 'SSL_SESSION_RESUMED': 'Resumed', 'HTTP_HOST': 'qcweb2.ssec.wisc.edu', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_SEC_CH_UA': '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', 'HTTP_SEC_CH_UA_MOBILE': '?0', 'HTTP_SEC_CH_UA_PLATFORM': '"macOS"', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'HTTP_SEC_FETCH_SITE': 'none', 'HTTP_SEC_FETCH_MODE': 'navigate', 'HTTP_SEC_FETCH_USER': '?1', 'HTTP_SEC_FETCH_DEST': 'document', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br, zstd', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'ELOQUA=GUID=61A25E7C6FDF4120B104E80CDC16BEEB; ELQSTATUS=OK; __gsas=ID=2d8e199b2946bc51:T=1731094010:RT=1731094010:S=ALNI_Ma_5YNiNoq0NlLt72y2RCPc1CEb_g; _hp2_props.3001039959=%7B%22Base.appName%22%3A%22Canvas%22%7D; _hp2_id.3001039959=%7B%22userId%22%3A%225978514486938310%22%2C%22pageviewId%22%3A%22440562338760070%22%2C%22sessionId%22%3A%221338963026909406%22%2C%22identity%22%3A%22uu-2-5ca2d709facfd50d34ed98183225e90857d8ffe9c3053efd43baa7bb47a36596-0oZnoj0EXwmozuvldCGvsOXhduumpWAJplTCVyht%22%2C%22trackerVersion%22%3A%224.0%22%2C%22identityField%22%3Anull%2C%22isIdentified%22%3A1%7D; _ga_7YGT91LS0G=GS1.1.1739309055.3.1.1739309087.0.0.0; _ga_8MJYP9RHDL=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_WV39VETPEH=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_W0KBLYZJ8L=GS1.1.1739548831.2.0.1739548848.0.0.0; _ga_DLGR3GYC5K=GS1.2.1739909437.1.0.1739909437.0.0.0; nmstat=40b68e16-6de2-14f7-2443-7e8c4cc20c1c; _ga=GA1.1.1637011006.1729780911; _ga_TNJJ6N9F4J=GS1.1.1741128288.3.1.1741128312.0.0.0; _clck=10n0iyd%7C2%7Cfu7%7C0%7C1882; _ga_PLJ9E3ZY82=GS1.1.1741966108.6.1.1741966144.0.0.0', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', 'SERVER_SIGNATURE': '', 'SERVER_SOFTWARE': 'Apache', 'SERVER_NAME': 'qcweb2.ssec.wisc.edu', 'SERVER_ADDR': '128.104.111.208', 'SERVER_PORT': '443', 'REMOTE_ADDR': '128.104.110.45', 'DOCUMENT_ROOT': '/var/www/html', 'REQUEST_SCHEME': 'https', 'CONTEXT_PREFIX': '', 'CONTEXT_DOCUMENT_ROOT': '/var/www/html', 'SERVER_ADMIN': '[no address given]', 'SCRIPT_FILENAME': '/var/www/html/web_internal/students/ygao/latency/assets/python/test.py', 'REMOTE_PORT': '54658', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'REQUEST_URI': '/web_internal/students/ygao/latency/assets/python/test.py', 'SCRIPT_NAME': '/web_internal/students/ygao/latency/assets/python/test.py', 'LC_CTYPE': 'C.UTF-8'}
+2025-03-18 20:14:01,723 - INFO - Attempting to import pandas...
+2025-03-18 20:14:02,096 - INFO - Attempting to import sat_latency.interface...
+2025-03-18 20:14:02,194 - INFO - Directory exists: True
+2025-03-18 20:14:02,194 - INFO - Directory readable: True
+2025-03-18 20:14:02,194 - INFO - Directory permissions: 0o755
+2025-03-18 20:14:02,195 - INFO - Directory owner: oper
+2025-03-18 20:14:02,195 - INFO - Directory group: domain users
+2025-03-18 20:14:02,195 - INFO - Successfully listed directory with 3 items
+2025-03-18 20:14:02,195 - INFO - First few items: ['2024', '2000', '2025']
+2025-03-18 20:14:04,672 - INFO - ================================================================================
+2025-03-18 20:14:04,672 - INFO - Test CGI script starting
+2025-03-18 20:14:04,672 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:14:04,672 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/test.py
+2025-03-18 20:14:04,672 - INFO - Python executable: /home/ygao/miniconda/envs/py39env/bin/python
+2025-03-18 20:14:04,672 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:14:04,672 - INFO - User running script: Unknown
+2025-03-18 20:14:04,672 - INFO - Environment variables: {'HTTPS': 'on', 'SSL_TLS_SNI': 'qcweb2.ssec.wisc.edu', 'SSL_SERVER_S_DN_C': 'US', 'SSL_SERVER_S_DN_ST': 'Wisconsin', 'SSL_SERVER_S_DN_O': 'University of Wisconsin-Madison', 'SSL_SERVER_S_DN_CN': 'ssec.wisc.edu', 'SSL_SERVER_I_DN_C': 'US', 'SSL_SERVER_I_DN_O': 'Internet2', 'SSL_SERVER_I_DN_CN': 'InCommon RSA Server CA 2', 'SSL_SERVER_SAN_DNS_0': 'ssec.wisc.edu', 'SSL_SERVER_SAN_DNS_1': '*.ssec.wisc.edu', 'SSL_VERSION_INTERFACE': 'mod_ssl/2.4.37', 'SSL_VERSION_LIBRARY': 'OpenSSL/1.1.1k', 'SSL_PROTOCOL': 'TLSv1.3', 'SSL_SECURE_RENEG': 'false', 'SSL_COMPRESS_METHOD': 'NULL', 'SSL_CIPHER': 'TLS_AES_256_GCM_SHA384', 'SSL_CIPHER_EXPORT': 'false', 'SSL_CIPHER_USEKEYSIZE': '256', 'SSL_CIPHER_ALGKEYSIZE': '256', 'SSL_CLIENT_VERIFY': 'NONE', 'SSL_SERVER_M_VERSION': '3', 'SSL_SERVER_M_SERIAL': '1A94C0029DE6D1DB1ED2F41A9D6CFFAF', 'SSL_SERVER_V_START': 'Jan  3 00:00:00 2025 GMT', 'SSL_SERVER_V_END': 'Jan  3 23:59:59 2026 GMT', 'SSL_SERVER_S_DN': 'CN=ssec.wisc.edu,O=University of Wisconsin-Madison,ST=Wisconsin,C=US', 'SSL_SERVER_I_DN': 'CN=InCommon RSA Server CA 2,O=Internet2,C=US', 'SSL_SERVER_A_KEY': 'rsaEncryption', 'SSL_SERVER_A_SIG': 'sha384WithRSAEncryption', 'SSL_SESSION_ID': '0ad21b6077ab99fa3a25e9216ad604f2288524007f70426888600c9b407d7e2d', 'SSL_SESSION_RESUMED': 'Resumed', 'HTTP_HOST': 'qcweb2.ssec.wisc.edu', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_SEC_CH_UA': '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', 'HTTP_SEC_CH_UA_MOBILE': '?0', 'HTTP_SEC_CH_UA_PLATFORM': '"macOS"', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'HTTP_SEC_FETCH_SITE': 'none', 'HTTP_SEC_FETCH_MODE': 'navigate', 'HTTP_SEC_FETCH_USER': '?1', 'HTTP_SEC_FETCH_DEST': 'document', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br, zstd', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'ELOQUA=GUID=61A25E7C6FDF4120B104E80CDC16BEEB; ELQSTATUS=OK; __gsas=ID=2d8e199b2946bc51:T=1731094010:RT=1731094010:S=ALNI_Ma_5YNiNoq0NlLt72y2RCPc1CEb_g; _hp2_props.3001039959=%7B%22Base.appName%22%3A%22Canvas%22%7D; _hp2_id.3001039959=%7B%22userId%22%3A%225978514486938310%22%2C%22pageviewId%22%3A%22440562338760070%22%2C%22sessionId%22%3A%221338963026909406%22%2C%22identity%22%3A%22uu-2-5ca2d709facfd50d34ed98183225e90857d8ffe9c3053efd43baa7bb47a36596-0oZnoj0EXwmozuvldCGvsOXhduumpWAJplTCVyht%22%2C%22trackerVersion%22%3A%224.0%22%2C%22identityField%22%3Anull%2C%22isIdentified%22%3A1%7D; _ga_7YGT91LS0G=GS1.1.1739309055.3.1.1739309087.0.0.0; _ga_8MJYP9RHDL=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_WV39VETPEH=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_W0KBLYZJ8L=GS1.1.1739548831.2.0.1739548848.0.0.0; _ga_DLGR3GYC5K=GS1.2.1739909437.1.0.1739909437.0.0.0; nmstat=40b68e16-6de2-14f7-2443-7e8c4cc20c1c; _ga=GA1.1.1637011006.1729780911; _ga_TNJJ6N9F4J=GS1.1.1741128288.3.1.1741128312.0.0.0; _clck=10n0iyd%7C2%7Cfu7%7C0%7C1882; _ga_PLJ9E3ZY82=GS1.1.1741966108.6.1.1741966144.0.0.0', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', 'SERVER_SIGNATURE': '', 'SERVER_SOFTWARE': 'Apache', 'SERVER_NAME': 'qcweb2.ssec.wisc.edu', 'SERVER_ADDR': '128.104.111.208', 'SERVER_PORT': '443', 'REMOTE_ADDR': '128.104.110.45', 'DOCUMENT_ROOT': '/var/www/html', 'REQUEST_SCHEME': 'https', 'CONTEXT_PREFIX': '', 'CONTEXT_DOCUMENT_ROOT': '/var/www/html', 'SERVER_ADMIN': '[no address given]', 'SCRIPT_FILENAME': '/var/www/html/web_internal/students/ygao/latency/assets/python/test.py', 'REMOTE_PORT': '54672', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'REQUEST_URI': '/web_internal/students/ygao/latency/assets/python/test.py', 'SCRIPT_NAME': '/web_internal/students/ygao/latency/assets/python/test.py', 'LC_CTYPE': 'C.UTF-8'}
+2025-03-18 20:14:04,672 - INFO - Attempting to import pandas...
+2025-03-18 20:14:05,020 - INFO - Attempting to import sat_latency.interface...
+2025-03-18 20:14:05,108 - INFO - Directory exists: True
+2025-03-18 20:14:05,108 - INFO - Directory readable: True
+2025-03-18 20:14:05,108 - INFO - Directory permissions: 0o755
+2025-03-18 20:14:05,109 - INFO - Directory owner: oper
+2025-03-18 20:14:05,109 - INFO - Directory group: domain users
+2025-03-18 20:14:05,109 - INFO - Successfully listed directory with 3 items
+2025-03-18 20:14:05,109 - INFO - First few items: ['2024', '2000', '2025']
+2025-03-18 20:14:07,309 - INFO - ================================================================================
+2025-03-18 20:14:07,309 - INFO - Test CGI script starting
+2025-03-18 20:14:07,309 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:14:07,309 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/test.py
+2025-03-18 20:14:07,309 - INFO - Python executable: /home/ygao/miniconda/envs/py39env/bin/python
+2025-03-18 20:14:07,309 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:14:07,309 - INFO - User running script: Unknown
+2025-03-18 20:14:07,310 - INFO - Environment variables: {'HTTPS': 'on', 'SSL_TLS_SNI': 'qcweb2.ssec.wisc.edu', 'SSL_SERVER_S_DN_C': 'US', 'SSL_SERVER_S_DN_ST': 'Wisconsin', 'SSL_SERVER_S_DN_O': 'University of Wisconsin-Madison', 'SSL_SERVER_S_DN_CN': 'ssec.wisc.edu', 'SSL_SERVER_I_DN_C': 'US', 'SSL_SERVER_I_DN_O': 'Internet2', 'SSL_SERVER_I_DN_CN': 'InCommon RSA Server CA 2', 'SSL_SERVER_SAN_DNS_0': 'ssec.wisc.edu', 'SSL_SERVER_SAN_DNS_1': '*.ssec.wisc.edu', 'SSL_VERSION_INTERFACE': 'mod_ssl/2.4.37', 'SSL_VERSION_LIBRARY': 'OpenSSL/1.1.1k', 'SSL_PROTOCOL': 'TLSv1.3', 'SSL_SECURE_RENEG': 'false', 'SSL_COMPRESS_METHOD': 'NULL', 'SSL_CIPHER': 'TLS_AES_256_GCM_SHA384', 'SSL_CIPHER_EXPORT': 'false', 'SSL_CIPHER_USEKEYSIZE': '256', 'SSL_CIPHER_ALGKEYSIZE': '256', 'SSL_CLIENT_VERIFY': 'NONE', 'SSL_SERVER_M_VERSION': '3', 'SSL_SERVER_M_SERIAL': '1A94C0029DE6D1DB1ED2F41A9D6CFFAF', 'SSL_SERVER_V_START': 'Jan  3 00:00:00 2025 GMT', 'SSL_SERVER_V_END': 'Jan  3 23:59:59 2026 GMT', 'SSL_SERVER_S_DN': 'CN=ssec.wisc.edu,O=University of Wisconsin-Madison,ST=Wisconsin,C=US', 'SSL_SERVER_I_DN': 'CN=InCommon RSA Server CA 2,O=Internet2,C=US', 'SSL_SERVER_A_KEY': 'rsaEncryption', 'SSL_SERVER_A_SIG': 'sha384WithRSAEncryption', 'SSL_SESSION_ID': '0ad21b6077ab99fa3a25e9216ad604f2288524007f70426888600c9b407d7e2d', 'SSL_SESSION_RESUMED': 'Resumed', 'HTTP_HOST': 'qcweb2.ssec.wisc.edu', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_SEC_CH_UA': '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"', 'HTTP_SEC_CH_UA_MOBILE': '?0', 'HTTP_SEC_CH_UA_PLATFORM': '"macOS"', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'HTTP_SEC_FETCH_SITE': 'none', 'HTTP_SEC_FETCH_MODE': 'navigate', 'HTTP_SEC_FETCH_USER': '?1', 'HTTP_SEC_FETCH_DEST': 'document', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br, zstd', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'ELOQUA=GUID=61A25E7C6FDF4120B104E80CDC16BEEB; ELQSTATUS=OK; __gsas=ID=2d8e199b2946bc51:T=1731094010:RT=1731094010:S=ALNI_Ma_5YNiNoq0NlLt72y2RCPc1CEb_g; _hp2_props.3001039959=%7B%22Base.appName%22%3A%22Canvas%22%7D; _hp2_id.3001039959=%7B%22userId%22%3A%225978514486938310%22%2C%22pageviewId%22%3A%22440562338760070%22%2C%22sessionId%22%3A%221338963026909406%22%2C%22identity%22%3A%22uu-2-5ca2d709facfd50d34ed98183225e90857d8ffe9c3053efd43baa7bb47a36596-0oZnoj0EXwmozuvldCGvsOXhduumpWAJplTCVyht%22%2C%22trackerVersion%22%3A%224.0%22%2C%22identityField%22%3Anull%2C%22isIdentified%22%3A1%7D; _ga_7YGT91LS0G=GS1.1.1739309055.3.1.1739309087.0.0.0; _ga_8MJYP9RHDL=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_WV39VETPEH=GS1.1.1739309087.3.1.1739309236.0.0.0; _ga_W0KBLYZJ8L=GS1.1.1739548831.2.0.1739548848.0.0.0; _ga_DLGR3GYC5K=GS1.2.1739909437.1.0.1739909437.0.0.0; nmstat=40b68e16-6de2-14f7-2443-7e8c4cc20c1c; _ga=GA1.1.1637011006.1729780911; _ga_TNJJ6N9F4J=GS1.1.1741128288.3.1.1741128312.0.0.0; _clck=10n0iyd%7C2%7Cfu7%7C0%7C1882; _ga_PLJ9E3ZY82=GS1.1.1741966108.6.1.1741966144.0.0.0', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', 'SERVER_SIGNATURE': '', 'SERVER_SOFTWARE': 'Apache', 'SERVER_NAME': 'qcweb2.ssec.wisc.edu', 'SERVER_ADDR': '128.104.111.208', 'SERVER_PORT': '443', 'REMOTE_ADDR': '128.104.110.45', 'DOCUMENT_ROOT': '/var/www/html', 'REQUEST_SCHEME': 'https', 'CONTEXT_PREFIX': '', 'CONTEXT_DOCUMENT_ROOT': '/var/www/html', 'SERVER_ADMIN': '[no address given]', 'SCRIPT_FILENAME': '/var/www/html/web_internal/students/ygao/latency/assets/python/test.py', 'REMOTE_PORT': '54672', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'REQUEST_URI': '/web_internal/students/ygao/latency/assets/python/test.py', 'SCRIPT_NAME': '/web_internal/students/ygao/latency/assets/python/test.py', 'LC_CTYPE': 'C.UTF-8'}
+2025-03-18 20:14:07,310 - INFO - Attempting to import pandas...
+2025-03-18 20:14:07,670 - INFO - Attempting to import sat_latency.interface...
+2025-03-18 20:14:07,762 - INFO - Directory exists: True
+2025-03-18 20:14:07,762 - INFO - Directory readable: True
+2025-03-18 20:14:07,762 - INFO - Directory permissions: 0o755
+2025-03-18 20:14:07,763 - INFO - Directory owner: oper
+2025-03-18 20:14:07,763 - INFO - Directory group: domain users
+2025-03-18 20:14:07,763 - INFO - Successfully listed directory with 3 items
+2025-03-18 20:14:07,763 - INFO - First few items: ['2024', '2000', '2025']
-- 
GitLab