File size: 1,780 Bytes
558db1e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import pandas as pd
import numpy as np
import json
import datetime
import decimal

def _ds(series, n=250):
    """Downsamples a time series to a maximum of `n` points for frontend rendering speed."""
    if series is None:
        return series
        
    # Note: Ensure it's a pandas Series to prevent .iloc crashes on raw lists/arrays
    if not isinstance(series, (pd.Series, pd.DataFrame)):
        series = pd.Series(series)
    elif not series.index.is_unique:
        # Note: Deduplicate index to prevent Chart.js from rendering zig-zag lines
        series = series[~series.index.duplicated(keep='last')]
    
    if len(series) <= n:
        return series
        
    # Note: Use uniform np.linspace to avoid discontinuity seams from concatenating tails
    idx = np.round(np.linspace(0, len(series) - 1, n)).astype(int)
    return series.iloc[idx]

def safe_json(obj):
    """

    Safely serializes deep dictionaries containing NumPy/Pandas native types, 

    Datetimes, NaNs, and NaTs for Chart.js parsing.

    """
    def _fix(o):
        if o is None or o is pd.NA or o is pd.NaT:
            return None
        if isinstance(o, decimal.Decimal):
            return float(o)
        if isinstance(o, (float, np.floating)):
            return None if not np.isfinite(o) else float(o)
        if isinstance(o, (int, np.integer)):
            return int(o)
        if isinstance(o, (pd.Timestamp, datetime.datetime, datetime.date)):
            return str(o)
        if isinstance(o, dict):
            return {k: _fix(v) for k, v in o.items()}
        if isinstance(o, (list, tuple, np.ndarray, pd.Series, pd.Index)):
            return [_fix(v) for v in o]
        return o
        
    return json.dumps(_fix(obj))