diff --git a/assets/python/data.py b/assets/python/data.py
index 7f4624fa8c2b8208b0d3d35d3e5ed21e3c67b403..9be786dc9802b240cb238f13b995f3854563c692 100755
--- a/assets/python/data.py
+++ b/assets/python/data.py
@@ -1,4 +1,4 @@
-#!/home/ygao/miniconda/envs/py39env/bin/python
+#!/home/oper/py39env/bin/python
 import cgi
 import cgitb
 import json
diff --git a/assets/python/generate_relationship.py b/assets/python/generate_relationship.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec64bd090283f2b39746d111a7239d3679a5cfc2
--- /dev/null
+++ b/assets/python/generate_relationship.py
@@ -0,0 +1,323 @@
+#!/usr/bin/env python3
+"""
+End-to-end script to generate a satellite relationships JSON file.
+This script:
+1. Automatically pulls data for the previous day using sat_latency_interface
+2. Processes the data to extract relationship information
+3. Generates the satellite_relationships.json file with consolidated IDs
+4. Handles satellite ID variations (e.g., G16/g16, DMSP-17/dmsp17, etc.)
+
+Example usage:
+    python generate_satellite_relationships.py -o satellite_relationships.json
+    python generate_satellite_relationships.py -d 2025-02-27 -o satellite_relationships.json
+"""
+
+import os
+import json
+import argparse
+import logging
+import subprocess
+import tempfile
+from datetime import datetime, timedelta
+from collections import defaultdict
+
+# Configure logging
+logging.basicConfig(
+    level=logging.INFO,
+    format='%(asctime)s - %(levelname)s - %(message)s'
+)
+logger = logging.getLogger(__name__)
+
+# Path to satellite data directory
+SATELLITE_DATA_DIR = "/data/sat_latency"
+
+# Hard-coded mapping of satellite ID variations to canonical IDs
+SATELLITE_ID_MAPPINGS = {
+    # Format: 'variant': 'canonical'
+    'G16': 'G16',
+    'g16': 'G16',
+    'G18': 'G18',
+    'g18': 'G18',
+    'G19': 'G19',
+    'g19': 'G19',
+    'DMSP-17': 'DMSP-17',
+    'dmsp17': 'DMSP-17',
+    'DMSP-18': 'DMSP-18',
+    'dmsp18': 'DMSP-18',
+    'DMSP-16': 'DMSP-16',
+    'dmsp16': 'DMSP-16',
+    'NOAA-19': 'NOAA-19',
+    'n19': 'NOAA-19',
+    'NOAA-20': 'NOAA-20',
+    'n20': 'NOAA-20',
+    'NOAA-21': 'NOAA-21',
+    'n21': 'NOAA-21',
+    'NOAA-18': 'NOAA-18',
+    'n18': 'NOAA-18',
+    'NOAA-15': 'NOAA-15',
+    'n15': 'NOAA-15',
+    # Add more mappings as needed
+}
+
+# Create reverse mapping (canonical to variants)
+CANONICAL_TO_VARIANTS = {}
+for variant, canonical in SATELLITE_ID_MAPPINGS.items():
+    if canonical not in CANONICAL_TO_VARIANTS:
+        CANONICAL_TO_VARIANTS[canonical] = []
+    CANONICAL_TO_VARIANTS[canonical].append(variant)
+
+def get_canonical_id(satellite_id):
+    """Get canonical ID for a satellite ID variant"""
+    return SATELLITE_ID_MAPPINGS.get(satellite_id, satellite_id)
+
+def get_previous_day():
+    """Get previous day's date in YYYY-MM-DD format"""
+    yesterday = datetime.now() - timedelta(days=1)
+    return yesterday.strftime('%Y-%m-%d')
+
+def run_sat_latency_query(date_str):
+    """
+    Run sat_latency_interface command to get data for the specified date.
+    
+    Args:
+        date_str: Date string in YYYY-MM-DD format
+        
+    Returns:
+        list: Data returned by sat_latency_interface or None if error
+    """
+    # Build start and end time strings
+    start_time = f"{date_str}T00:00:00"
+    end_time = f"{date_str}T23:59:59"
+    
+    # Build the command
+    base_cmd = "module load miniconda/3.6-base && source activate ~/.mdrexler_conda && sat_latency_interface"
+    # Include all fields to ensure we get satellite_id
+    cmd = f"{base_cmd} -d {SATELLITE_DATA_DIR} --from '{start_time}' --until '{end_time}' --output-type json"
+    
+    logger.info(f"Running command: {cmd}")
+    
+    try:
+        # Create a temporary shell script to run the command
+        with tempfile.NamedTemporaryFile(suffix='.sh', mode='w', delete=False) as temp_script:
+            script_path = temp_script.name
+            temp_script.write("#!/bin/bash\n")
+            temp_script.write(cmd + "\n")
+        
+        # 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]
+        
+        logger.info(f"Executing: {' '.join(sudo_cmd)}")
+        
+        # Use PIPE for stdout and stderr
+        process = subprocess.Popen(
+            sudo_cmd, 
+            stdout=subprocess.PIPE, 
+            stderr=subprocess.PIPE,
+            universal_newlines=True
+        )
+        
+        # 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 None
+        
+        # Log the first part of the output
+        if stdout:
+            logger.info(f"Command output (first 200 chars): {stdout[:200]}...")
+        else:
+            logger.warning("Command returned empty output")
+            return None
+            
+        # 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]}...")
+            return None
+            
+    except Exception as e:
+        logger.error(f"Error executing command: {str(e)}")
+        return None
+    finally:
+        # Clean up temporary script
+        if os.path.exists(script_path):
+            os.remove(script_path)
+
+def extract_relationships_from_data(data):
+    """
+    Extract relationship information from satellite data.
+    
+    Args:
+        data: List of satellite data records
+        
+    Returns:
+        dict: Structured relationship information
+    """
+    if not data:
+        logger.error("No data to process")
+        return None
+        
+    # Log sample of the data to debug column names
+    if data and len(data) > 0:
+        logger.info(f"Sample data record keys: {list(data[0].keys())}")
+        logger.info(f"Sample data record: {json.dumps(data[0], indent=2)}")
+    
+    # Initialize collections
+    all_satellites = set()
+    all_coverages = set()
+    all_instruments = set()
+    relationships = defaultdict(lambda: {
+        'coverages': set(),
+        'instruments': set(),
+        'coverage_instruments': defaultdict(set)
+    })
+    
+    # Process each record
+    for record in data:
+        # Extract satellite ID (handle case variations in column name)
+        satellite_id = record.get('satellite_id') or record.get('satellite_ID') or record.get('SATELLITE_ID')
+        if not satellite_id:
+            satellite_id = 'Not Available'
+        
+        # Extract coverage and instrument (handle case variations and null values)
+        coverage = record.get('coverage') or record.get('COVERAGE')
+        if not coverage or coverage is None:
+            coverage = 'Not Available'
+        
+        instrument = record.get('instrument') or record.get('INSTRUMENT')
+        if not instrument or instrument is None:
+            instrument = 'Not Available'
+        
+        # Get canonical satellite ID
+        canonical_id = get_canonical_id(satellite_id)
+        
+        # Add to collections
+        all_satellites.add(satellite_id)  # Track original IDs first
+        all_coverages.add(coverage)
+        all_instruments.add(instrument)
+        
+        # Update relationships for the canonical ID
+        relationships[canonical_id]['coverages'].add(coverage)
+        relationships[canonical_id]['instruments'].add(instrument)
+        relationships[canonical_id]['coverage_instruments'][coverage].add(instrument)
+    
+    # Convert sets to lists for JSON serialization
+    result = {
+        "satellites": [],  # We'll populate this with consolidated IDs
+        "coverages": sorted(list(all_coverages)),
+        "instruments": sorted(list(all_instruments)),
+        "relationships": {}
+    }
+    
+    # Group satellites by canonical ID
+    satellite_groups = defaultdict(list)
+    for sat_id in all_satellites:
+        canonical_id = get_canonical_id(sat_id)
+        satellite_groups[canonical_id].append(sat_id)
+    
+    # Use canonical IDs as the satellite list
+    result["satellites"] = sorted(list(satellite_groups.keys()))
+    
+    # Add satellite variant information
+    result["satellite_variants"] = {
+        canonical: variants for canonical, variants in satellite_groups.items()
+    }
+    
+    # Convert relationships to lists for JSON serialization
+    for sat_id, rel in relationships.items():
+        result["relationships"][sat_id] = {
+            "coverages": sorted(list(rel["coverages"])),
+            "instruments": sorted(list(rel["instruments"])),
+            "coverage_instruments": {
+                cov: sorted(list(instruments)) 
+                for cov, instruments in rel["coverage_instruments"].items()
+            }
+        }
+    
+    return result
+
+def generate_satellite_relationships(date_str=None):
+    """
+    Generate satellite relationships JSON by querying data for the specified date.
+    
+    Args:
+        date_str: Date string in YYYY-MM-DD format, or None for previous day
+        
+    Returns:
+        dict: Structured relationship information
+    """
+    # Use previous day if no date provided
+    if not date_str:
+        date_str = get_previous_day()
+    
+    logger.info(f"Generating satellite relationships for date: {date_str}")
+    
+    # Fetch data for the specified date
+    data = run_sat_latency_query(date_str)
+    
+    if not data:
+        logger.error(f"Failed to get data for {date_str}")
+        return None
+    
+    # Extract relationships from data
+    result = extract_relationships_from_data(data)
+    
+    if not result:
+        logger.error("Failed to extract relationships from data")
+        return None
+    
+    return result
+
+def save_relationships_json(relationships, output_file):
+    """
+    Save relationships JSON to file.
+    
+    Args:
+        relationships: Structured relationship information
+        output_file: Output file path
+        
+    Returns:
+        bool: True if successful, False otherwise
+    """
+    try:
+        with open(output_file, 'w') as f:
+            json.dump(relationships, f, indent=2)
+        
+        logger.info(f"Successfully wrote relationships to {output_file}")
+        logger.info(f"Found {len(relationships['satellites'])} satellites, "
+                   f"{len(relationships['coverages'])} coverages, "
+                   f"{len(relationships['instruments'])} instruments")
+        return True
+    except Exception as e:
+        logger.error(f"Error writing output file: {str(e)}")
+        return False
+
+def main():
+    parser = argparse.ArgumentParser(description='Generate satellite relationships JSON.')
+    parser.add_argument('-d', '--date', help='Date to query (YYYY-MM-DD). Defaults to previous day')
+    parser.add_argument('-o', '--output', default='satellite_relationships.json', help='Output JSON file path')
+    
+    args = parser.parse_args()
+    
+    # Generate relationships JSON
+    result = generate_satellite_relationships(args.date)
+    
+    if not result:
+        logger.error("Failed to generate relationships JSON")
+        return
+    
+    # Save to file
+    if not save_relationships_json(result, args.output):
+        logger.error(f"Failed to save relationships to {args.output}")
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/assets/python/latency_viewer.log b/assets/python/latency_viewer.log
index fed982ad91936780498ae81dfb8bd81636ca43bb..ea8ff76c30588121a0ffedc5a2210b1446190713 100644
--- a/assets/python/latency_viewer.log
+++ b/assets/python/latency_viewer.log
@@ -1085,3 +1085,219 @@ ValueError: Must pass schema, or at least one RecordBatch
 2025-03-18 20:39:15,464 - INFO - Query returned: 0 records
 2025-03-18 20:39:15,464 - INFO - Query returned no data
 2025-03-18 20:39:15,464 - INFO - Returning response with status code 200 and 0 records
+2025-03-18 20:42:03,119 - INFO - ================================================================================
+2025-03-18 20:42:03,119 - INFO - Data.py script starting
+2025-03-18 20:42:03,120 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:42:03,120 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:42:03,120 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:42:03,120 - INFO - User running script: Unknown
+2025-03-18 20:42:03,120 - 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:42:03,120 - INFO - Directory contents: ['satellite_relationships.json', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log', 'data.py', 'metadata.py', 'sat_db_functions.py', 'satellites.py']
+2025-03-18 20:42:03,205 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:42:03,205 - INFO - Calling data_endpoint function...
+2025-03-18 20:42:03,206 - 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:42:03,206 - INFO - Filter parameters: satellite_id=4B, coverage=None, instrument=GIIRS
+2025-03-18 20:42:03,206 - INFO - Getting canonical form for: 4B
+2025-03-18 20:42:03,206 - INFO - Canonical ID: 4B
+2025-03-18 20:42:03,206 - INFO - All variants: ['4B']
+2025-03-18 20:42:03,206 - INFO - Expanded satellite ID 4B to variants: ['4B']
+2025-03-18 20:42:03,206 - 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:42:03,206 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:42:03,206 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:42:03,206 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:42:03,206 - 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:42:03,206 - INFO - Filters: satellite_ids=['4B'], coverage=None, instrument=['GIIRS']
+2025-03-18 20:42:03,490 - INFO - Successfully converted data: 3008 records found
+2025-03-18 20:42:03,491 - INFO - Query returned: 3008 records
+2025-03-18 20:42:03,491 - INFO - Converting to DataFrame...
+2025-03-18 20:42:03,495 - INFO - DataFrame created with shape: (3008, 12)
+2025-03-18 20:42:03,495 - INFO - Processing DataFrame...
+2025-03-18 20:42:03,495 - 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:42:03,495 - INFO - Cleaning latency data...
+2025-03-18 20:42:03,497 - INFO - DataFrame shape after cleaning: (3008, 12)
+2025-03-18 20:42:03,501 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:42:03,502 - INFO - Converting timestamps...
+2025-03-18 20:42:03,516 - INFO - Converting to records...
+2025-03-18 20:42:03,541 - INFO - Created 3008 result records
+2025-03-18 20:42:03,542 - INFO - Returning response with status code 200 and 3008 records
+2025-03-18 20:42:08,664 - INFO - ================================================================================
+2025-03-18 20:42:08,664 - INFO - Data.py script starting
+2025-03-18 20:42:08,664 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:42:08,664 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:42:08,664 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:42:08,664 - INFO - User running script: Unknown
+2025-03-18 20:42:08,664 - 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:42:08,664 - INFO - Directory contents: ['satellite_relationships.json', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log', 'data.py', 'metadata.py', 'sat_db_functions.py', 'satellites.py']
+2025-03-18 20:42:08,752 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:42:08,752 - INFO - Calling data_endpoint function...
+2025-03-18 20:42:08,752 - INFO - Query parameters: start_date=2025-03-17, end_date=2025-03-17, start_hour=00:00, end_hour=23:59
+2025-03-18 20:42:08,752 - INFO - Filter parameters: satellite_id=4B, coverage=None, instrument=GIIRS
+2025-03-18 20:42:08,752 - INFO - Getting canonical form for: 4B
+2025-03-18 20:42:08,752 - INFO - Canonical ID: 4B
+2025-03-18 20:42:08,752 - INFO - All variants: ['4B']
+2025-03-18 20:42:08,753 - INFO - Expanded satellite ID 4B to variants: ['4B']
+2025-03-18 20:42:08,753 - INFO - Data request - Period: 2025-03-17T00:00:00 to 2025-03-17T23:59:59, Filters: {'satellite-id': ['4B'], 'instrument': 'GIIRS'}
+2025-03-18 20:42:08,753 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:42:08,753 - INFO - Querying satellite latency data from 2025-03-17T00:00:00 to 2025-03-17T23:59:59
+2025-03-18 20:42:08,753 - INFO - Converted timestamps: 2025-03-17 00:00:00+00:00 to 2025-03-17 23:59:59+00:00
+2025-03-18 20:42:08,753 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-17 00:00:00+00:00, end_date=2025-03-17 23:59:59+00:00
+2025-03-18 20:42:08,753 - INFO - Filters: satellite_ids=['4B'], coverage=None, instrument=['GIIRS']
+2025-03-18 20:42:09,103 - INFO - Successfully converted data: 3580 records found
+2025-03-18 20:42:09,105 - INFO - Query returned: 3580 records
+2025-03-18 20:42:09,105 - INFO - Converting to DataFrame...
+2025-03-18 20:42:09,110 - INFO - DataFrame created with shape: (3580, 12)
+2025-03-18 20:42:09,110 - INFO - Processing DataFrame...
+2025-03-18 20:42:09,110 - 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:42:09,110 - INFO - Cleaning latency data...
+2025-03-18 20:42:09,112 - INFO - DataFrame shape after cleaning: (3580, 12)
+2025-03-18 20:42:09,116 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:42:09,117 - INFO - Converting timestamps...
+2025-03-18 20:42:09,134 - INFO - Converting to records...
+2025-03-18 20:42:09,164 - INFO - Created 3580 result records
+2025-03-18 20:42:09,165 - INFO - Returning response with status code 200 and 3580 records
+2025-03-18 20:43:28,417 - INFO - ================================================================================
+2025-03-18 20:43:28,417 - INFO - Data.py script starting
+2025-03-18 20:43:28,417 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:43:28,417 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:43:28,417 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:43:28,417 - INFO - User running script: Unknown
+2025-03-18 20:43:28,417 - 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:43:28,417 - INFO - Directory contents: ['satellite_relationships.json', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log', 'data.py', 'metadata.py', 'sat_db_functions.py', 'satellites.py']
+2025-03-18 20:43:28,516 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:43:28,516 - INFO - Calling data_endpoint function...
+2025-03-18 20:43:28,517 - INFO - Query parameters: start_date=2025-03-17, end_date=2025-03-17, start_hour=00:00, end_hour=23:59
+2025-03-18 20:43:28,517 - INFO - Filter parameters: satellite_id=G16, coverage=None, instrument=ABI
+2025-03-18 20:43:28,517 - INFO - Getting canonical form for: G16
+2025-03-18 20:43:28,517 - INFO - Canonical ID: G16
+2025-03-18 20:43:28,517 - INFO - All variants: ['G16', 'g16']
+2025-03-18 20:43:28,517 - INFO - Expanded satellite ID G16 to variants: ['G16', 'g16']
+2025-03-18 20:43:28,517 - INFO - Data request - Period: 2025-03-17T00:00:00 to 2025-03-17T23:59:59, Filters: {'satellite-id': ['G16', 'g16'], 'instrument': 'ABI'}
+2025-03-18 20:43:28,517 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:43:28,517 - INFO - Querying satellite latency data from 2025-03-17T00:00:00 to 2025-03-17T23:59:59
+2025-03-18 20:43:28,517 - INFO - Converted timestamps: 2025-03-17 00:00:00+00:00 to 2025-03-17 23:59:59+00:00
+2025-03-18 20:43:28,517 - INFO - Query parameters: database=/data/sat_latency, start_date=2025-03-17 00:00:00+00:00, end_date=2025-03-17 23:59:59+00:00
+2025-03-18 20:43:28,517 - INFO - Filters: satellite_ids=['g16', 'G16'], coverage=None, instrument=['ABI']
+2025-03-18 20:43:35,104 - INFO - Successfully converted data: 331761 records found
+2025-03-18 20:43:35,200 - INFO - Query returned: 331761 records
+2025-03-18 20:43:35,200 - INFO - Converting to DataFrame...
+2025-03-18 20:43:35,688 - INFO - DataFrame created with shape: (331761, 12)
+2025-03-18 20:43:35,688 - INFO - Processing DataFrame...
+2025-03-18 20:43:35,689 - 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:43:35,689 - INFO - Cleaning latency data...
+2025-03-18 20:43:35,792 - INFO - DataFrame shape after cleaning: (331761, 12)
+2025-03-18 20:43:36,026 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:43:36,103 - INFO - Converting timestamps...
+2025-03-18 20:43:37,821 - INFO - Converting to records...
+2025-03-18 20:43:41,365 - INFO - Created 331761 result records
+2025-03-18 20:43:41,578 - INFO - Returning response with status code 200 and 331761 records
+2025-03-18 20:53:06,558 - INFO - ================================================================================
+2025-03-18 20:53:06,558 - INFO - Data.py script starting
+2025-03-18 20:53:06,558 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 20:53:06,558 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 20:53:06,558 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 20:53:06,559 - INFO - User running script: Unknown
+2025-03-18 20:53:06,559 - 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:53:06,559 - INFO - Directory contents: ['satellite_relationships.json', '.htaccess', 'test.py', 'test_log', 'latency_viewer.log', 'data.py', 'metadata.py', 'sat_db_functions.py', 'satellites.py']
+2025-03-18 20:53:06,659 - INFO - Successfully imported from sat_db_functions
+2025-03-18 20:53:06,659 - INFO - Calling data_endpoint function...
+2025-03-18 20:53:06,660 - 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:53:06,660 - INFO - Filter parameters: satellite_id=M10, coverage=None, instrument=seviri
+2025-03-18 20:53:06,660 - INFO - Getting canonical form for: M10
+2025-03-18 20:53:06,660 - INFO - Canonical ID: M10
+2025-03-18 20:53:06,660 - INFO - All variants: ['M10']
+2025-03-18 20:53:06,660 - INFO - Expanded satellite ID M10 to variants: ['M10']
+2025-03-18 20:53:06,660 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['M10'], 'instrument': 'seviri'}
+2025-03-18 20:53:06,660 - INFO - About to call run_sat_latency_query...
+2025-03-18 20:53:06,660 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 20:53:06,660 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 20:53:06,660 - 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:53:06,660 - INFO - Filters: satellite_ids=['M10'], coverage=None, instrument=['seviri']
+2025-03-18 20:53:07,186 - INFO - Successfully converted data: 19127 records found
+2025-03-18 20:53:07,191 - INFO - Query returned: 19127 records
+2025-03-18 20:53:07,191 - INFO - Converting to DataFrame...
+2025-03-18 20:53:07,216 - INFO - DataFrame created with shape: (19127, 12)
+2025-03-18 20:53:07,216 - INFO - Processing DataFrame...
+2025-03-18 20:53:07,216 - 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:53:07,216 - INFO - Cleaning latency data...
+2025-03-18 20:53:07,221 - INFO - DataFrame shape after cleaning: (19127, 12)
+2025-03-18 20:53:07,231 - INFO - Adding canonical_satellite_id column...
+2025-03-18 20:53:07,236 - INFO - Converting timestamps...
+2025-03-18 20:53:07,341 - INFO - Converting to records...
+2025-03-18 20:53:07,515 - INFO - Created 19127 result records
+2025-03-18 20:53:07,523 - INFO - Returning response with status code 200 and 19127 records
+2025-03-18 21:03:20,482 - INFO - ================================================================================
+2025-03-18 21:03:20,482 - INFO - Data.py script starting
+2025-03-18 21:03:20,483 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 21:03:20,483 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 21:03:20,483 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 21:03:20,483 - INFO - User running script: Unknown
+2025-03-18 21:03:20,483 - 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 21:03:20,483 - INFO - Directory contents: ['satellite_relationships.json', '.htaccess', 'latency_viewer.log', 'data.py', 'metadata.py', 'sat_db_functions.py', 'satellites.py', 'generate_relationship.py']
+2025-03-18 21:03:20,583 - INFO - Successfully imported from sat_db_functions
+2025-03-18 21:03:20,583 - INFO - Calling data_endpoint function...
+2025-03-18 21:03:20,583 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 21:03:20,583 - INFO - Filter parameters: satellite_id=4B, coverage=None, instrument=GIIRS
+2025-03-18 21:03:20,583 - INFO - Getting canonical form for: 4B
+2025-03-18 21:03:20,583 - INFO - Canonical ID: 4B
+2025-03-18 21:03:20,583 - INFO - All variants: ['4B']
+2025-03-18 21:03:20,583 - INFO - Expanded satellite ID 4B to variants: ['4B']
+2025-03-18 21:03:20,583 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['4B'], 'instrument': 'GIIRS'}
+2025-03-18 21:03:20,583 - INFO - About to call run_sat_latency_query...
+2025-03-18 21:03:20,583 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 21:03:20,583 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 21:03:20,583 - 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 21:03:20,583 - INFO - Filters: satellite_ids=['4B'], coverage=None, instrument=['GIIRS']
+2025-03-18 21:03:20,913 - INFO - Successfully converted data: 3038 records found
+2025-03-18 21:03:20,915 - INFO - Query returned: 3038 records
+2025-03-18 21:03:20,915 - INFO - Converting to DataFrame...
+2025-03-18 21:03:20,919 - INFO - DataFrame created with shape: (3038, 12)
+2025-03-18 21:03:20,919 - INFO - Processing DataFrame...
+2025-03-18 21:03:20,919 - 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 21:03:20,919 - INFO - Cleaning latency data...
+2025-03-18 21:03:20,922 - INFO - DataFrame shape after cleaning: (3038, 12)
+2025-03-18 21:03:20,925 - INFO - Adding canonical_satellite_id column...
+2025-03-18 21:03:20,927 - INFO - Converting timestamps...
+2025-03-18 21:03:20,943 - INFO - Converting to records...
+2025-03-18 21:03:20,972 - INFO - Created 3038 result records
+2025-03-18 21:03:20,973 - INFO - Returning response with status code 200 and 3038 records
+2025-03-18 21:04:08,659 - INFO - ================================================================================
+2025-03-18 21:04:08,659 - INFO - Data.py script starting
+2025-03-18 21:04:08,659 - INFO - Current working directory: /home/ygao/latency/assets/python
+2025-03-18 21:04:08,659 - INFO - Script path: /var/www/html/web_internal/students/ygao/latency/assets/python/data.py
+2025-03-18 21:04:08,659 - INFO - Python version: 3.9.21 (main, Dec 11 2024, 16:24:11) 
+[GCC 11.2.0]
+2025-03-18 21:04:08,660 - INFO - User running script: Unknown
+2025-03-18 21:04:08,660 - 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 21:04:08,660 - INFO - Directory contents: ['satellite_relationships.json', '.htaccess', 'latency_viewer.log', 'data.py', 'metadata.py', 'sat_db_functions.py', 'satellites.py', 'generate_relationship.py']
+2025-03-18 21:04:09,069 - INFO - Successfully imported from sat_db_functions
+2025-03-18 21:04:09,069 - INFO - Calling data_endpoint function...
+2025-03-18 21:04:09,069 - INFO - Query parameters: start_date=2025-03-18, end_date=2025-03-18, start_hour=00:00, end_hour=23:59
+2025-03-18 21:04:09,069 - INFO - Filter parameters: satellite_id=4B, coverage=None, instrument=GIIRS
+2025-03-18 21:04:09,069 - INFO - Getting canonical form for: 4B
+2025-03-18 21:04:09,069 - INFO - Canonical ID: 4B
+2025-03-18 21:04:09,070 - INFO - All variants: ['4B']
+2025-03-18 21:04:09,070 - INFO - Expanded satellite ID 4B to variants: ['4B']
+2025-03-18 21:04:09,070 - INFO - Data request - Period: 2025-03-18T00:00:00 to 2025-03-18T23:59:59, Filters: {'satellite-id': ['4B'], 'instrument': 'GIIRS'}
+2025-03-18 21:04:09,070 - INFO - About to call run_sat_latency_query...
+2025-03-18 21:04:09,070 - INFO - Querying satellite latency data from 2025-03-18T00:00:00 to 2025-03-18T23:59:59
+2025-03-18 21:04:09,070 - INFO - Converted timestamps: 2025-03-18 00:00:00+00:00 to 2025-03-18 23:59:59+00:00
+2025-03-18 21:04:09,070 - 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 21:04:09,070 - INFO - Filters: satellite_ids=['4B'], coverage=None, instrument=['GIIRS']
+2025-03-18 21:04:09,384 - INFO - Successfully converted data: 3038 records found
+2025-03-18 21:04:09,386 - INFO - Query returned: 3038 records
+2025-03-18 21:04:09,386 - INFO - Converting to DataFrame...
+2025-03-18 21:04:09,390 - INFO - DataFrame created with shape: (3038, 12)
+2025-03-18 21:04:09,390 - INFO - Processing DataFrame...
+2025-03-18 21:04:09,390 - 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 21:04:09,390 - INFO - Cleaning latency data...
+2025-03-18 21:04:09,393 - INFO - DataFrame shape after cleaning: (3038, 12)
+2025-03-18 21:04:09,396 - INFO - Adding canonical_satellite_id column...
+2025-03-18 21:04:09,398 - INFO - Converting timestamps...
+2025-03-18 21:04:09,415 - INFO - Converting to records...
+2025-03-18 21:04:09,444 - INFO - Created 3038 result records
+2025-03-18 21:04:09,445 - INFO - Returning response with status code 200 and 3038 records
diff --git a/assets/python/sat_db_functions.py b/assets/python/sat_db_functions.py
index f0e8647d9a2e67209ac4e8089262d02148e369c3..d39b1e6ae0f976f3a026a50a1f078e47dfccad74 100755
--- a/assets/python/sat_db_functions.py
+++ b/assets/python/sat_db_functions.py
@@ -1,4 +1,4 @@
-#!/home/ygao/miniconda/envs/py39env/bin/python
+#!/home/oper/py39env/bin/python
 import os
 import json
 import logging
diff --git a/assets/python/satellites.py b/assets/python/satellites.py
index e8bb782a1ffb48005ace1ba4d54f1ff402a26c48..19d8dc3d108806d24b22a4defd36cd806d266eaf 100755
--- a/assets/python/satellites.py
+++ b/assets/python/satellites.py
@@ -1,4 +1,4 @@
-#!/home/ygao/miniconda/envs/py39env/bin/python
+#!/home/oper/py39env/bin/python
 import cgi
 import json
 import sys
diff --git a/assets/python/test.py b/assets/python/test.py
deleted file mode 100755
index e7e329b1aa2b85d5077175b000dbf1bc17dc892e..0000000000000000000000000000000000000000
--- a/assets/python/test.py
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/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
deleted file mode 100644
index 11e4fd8b629b5cb6f6394afba1c82bd55c4140dc..0000000000000000000000000000000000000000
--- a/assets/python/test_log
+++ /dev/null
@@ -1,90 +0,0 @@
-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']