import pandas as pd
import numpy as np
import datetime
import traceback
from backblaze import *
# ============================================================
# NUMBER FORMATTING HELPERS
# ============================================================
def format_number(num):
if num is None:
return "-"
try:
return f"{float(num):,.2f}".rstrip("0").rstrip(".")
except:
return str(num)
def format_large_number(num):
if num is None:
return "-"
try:
n = float(num)
if abs(n) >= 1_00_00_000: # Crore
return f"{n/1_00_00_000:.2f} Cr"
elif abs(n) >= 1_00_000: # Lakh
return f"{n/1_00_000:.2f} L"
elif abs(n) >= 1_000: # Thousand
return f"{n/1_000:.2f} K"
else:
return format_number(n)
except:
return str(num)
# ============================================================
# HTML UI HELPERS
# ============================================================
def html_card(title, content):
return f"""
"""
def html_section(title, content):
return f"""
{title}
{content}
"""
def html_error(msg):
return f"""
Error: {msg}
"""
# ============================================================
# DATAFRAME CLEANING
# ============================================================
def clean_df(df):
if isinstance(df.index, pd.DatetimeIndex):
df.index = df.index.strftime("%Y-%m-%d")
df.replace([np.inf, -np.inf], np.nan, inplace=True)
df.fillna("-", inplace=True)
return df
# ============================================================
# TABLE STYLING
# ============================================================
def make_table(df):
try:
df = df.copy()
df = clean_df(df)
html = df.to_html(classes="styled-table", escape=False, border=0)
return f"""
{html}
"""
except Exception as e:
return html_error(f"Table render failed: {e}
{traceback.format_exc()}")
# ============================================================
# UNIVERSAL PLOT WRAPPER
# ============================================================
def wrap_plotly_html(html_chart, table_html=None):
extra = f"{table_html}
" if table_html else ""
return f"""
{html_card("Chart", html_chart)}
{extra}
"""
# ============================================================
# INDICATOR SAFE EXTRACTION HELPER
# ============================================================
def safe_get(df, key, default_val="-"):
try:
return df.get(key, default_val)
except:
return default_val
def format_timestamp_to_date(timestamp):
if not isinstance(timestamp, (int, float)) or timestamp <= 0:
return "N/A"
try:
return datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')
except:
return "Invalid Date"
# ============================================================
# HTML WRAPPER
# ============================================================
STYLE_BLOCK = """
"""
def wrap_html(content, title="Stock Data"):
return f"""
{title}
{STYLE_BLOCK}
{content}
"""
# ======================================================
# Scrollable HTML wrapper
# ======================================================
SCROLL_WRAP = """
{{HTML}}
"""
# ======================================================
# Date helpers
# ======================================================
def today_str():
return datetime.date.today().strftime("%d-%m-%Y")
def yesterday_str():
return (datetime.date.today() - datetime.timedelta(days=1)).strftime("%d-%m-%Y")
import datetime
def last_year_str(d: str) -> str:
"""
Input : DD-MM-YYYY
Output : (same date last year) + 1 day
→ max difference = 364 days
"""
dt = datetime.datetime.strptime(d, "%d-%m-%Y")
try:
last_year = dt.replace(year=dt.year - 1)
except ValueError:
# Handles 29 Feb → 28 Feb
last_year = dt.replace(year=dt.year - 1, day=28)
last_year_plus_one = last_year + datetime.timedelta(days=1)
return last_year_plus_one.strftime("%d-%m-%Y")
# ======================================================
# HTML wrapper
# ======================================================
def wrap(html):
if html is None:
return "No Data
"
return SCROLL_WRAP.replace("{{HTML}}", html)