eshan6704 commited on
Commit
f09ef01
·
verified ·
1 Parent(s): 8e82e4f

Update preopen_html.py

Browse files
Files changed (1) hide show
  1. preopen_html.py +206 -161
preopen_html.py CHANGED
@@ -1,22 +1,28 @@
1
- from nsepython import *
2
  import pandas as pd
3
- import re
 
4
  from datetime import datetime as dt
 
5
 
6
- # persist helpers (ALREADY EXIST IN YOUR PROJECT)
7
  from persist import exists, load, save
8
 
9
 
10
- def build_preopen_html(key="NIFTY"):
11
  """
12
- Build full Pre-Open HTML with daily cache.
13
- If cached HTML exists for today → return it.
14
- Else fetch, rebuild, save, return.
 
 
 
 
15
  """
16
 
17
  # ================= CACHE =================
18
  today = dt.now().strftime("%Y-%m-%d")
19
- cache_key = f"preopen_html_{key}"
20
 
21
  if exists(cache_key):
22
  cached = load(cache_key)
@@ -24,164 +30,203 @@ def build_preopen_html(key="NIFTY"):
24
  return cached.get("html")
25
 
26
  # ================= FETCH DATA =================
27
- p = nsefetch(f"https://www.nseindia.com/api/market-data-pre-open?key={key}")
28
-
29
- data_df = df_from_data(p.pop("data"))
30
- rem_df = df_from_data([p])
31
-
32
- main_df = data_df.iloc[[0]] if not data_df.empty else pd.DataFrame()
33
- const_df = data_df.iloc[1:] if len(data_df) > 1 else pd.DataFrame()
34
-
35
- # ================= REMOVE PATTERN COLUMNS =================
36
- pattern_remove = re.compile(r"^(price_|buyQty_|sellQty_|iep_)\d+$")
37
-
38
- def remove_pattern_cols(df):
39
- if df is None or df.empty:
40
- return df
41
- return df[[c for c in df.columns if not pattern_remove.match(c)]]
42
-
43
- main_df = remove_pattern_cols(main_df)
44
- const_df = remove_pattern_cols(const_df)
45
- rem_df = remove_pattern_cols(rem_df)
46
-
47
- # ================= TABLE COLOR HELPER =================
48
- def df_to_html_color(df, metric_col=None):
49
- if df is None or df.empty:
50
- return "<i>No data</i>"
51
-
52
- df_html = df.copy()
53
- top3_up, top3_down = [], []
54
-
55
- if metric_col and metric_col in df_html.columns:
56
- if pd.api.types.is_numeric_dtype(df_html[metric_col]):
57
- col_numeric = df_html[metric_col].dropna()
58
- top3_up = col_numeric.nlargest(3).index.tolist()
59
- top3_down = col_numeric.nsmallest(3).index.tolist()
60
-
61
- for idx, row in df_html.iterrows():
62
- for col in df_html.columns:
63
- val = row[col]
64
- style = ""
65
-
66
- if isinstance(val, (int, float)):
67
- val_fmt = f"{val:.2f}"
68
- if val > 0:
69
- style = "numeric-positive"
70
- elif val < 0:
71
- style = "numeric-negative"
72
-
73
- if col == metric_col:
74
- if idx in top3_up:
75
- style += " top-up"
76
- elif idx in top3_down:
77
- style += " top-down"
78
-
79
- df_html.at[idx, col] = f'<span class="{style.strip()}">{val_fmt}</span>'
80
- else:
81
- df_html.at[idx, col] = str(val)
82
-
83
- return df_html.to_html(index=False, escape=False, classes="compact-table")
84
-
85
- # ================= MINI CARDS =================
86
- def build_info_cards(rem_df, main_df):
87
- combined = pd.concat([rem_df, main_df], axis=1)
88
- combined = combined.loc[:, ~combined.columns.duplicated()]
89
- combined = remove_pattern_cols(combined)
90
-
91
- cards = '<div class="mini-card-container">'
92
- for col in combined.columns:
93
- val = combined.at[0, col] if not combined.empty else ""
94
- cards += f"""
95
- <div class="mini-card">
96
- <div class="card-key">{col}</div>
97
- <div class="card-val">{val}</div>
98
- </div>
99
- """
100
- cards += '</div>'
101
- return cards
102
-
103
- info_cards_html = build_info_cards(rem_df, main_df)
104
-
105
- # ================= CONSTITUENTS TABLE =================
106
- cons_html = df_to_html_color(const_df)
107
-
108
- # ================= METRIC TABLES =================
109
- metric_cols_allowed = [
110
- "pChange",
111
- "totalTurnover",
112
- "marketCap",
113
- "totalTradedVolume"
114
- ]
115
-
116
- metric_tables = ""
117
- for col in metric_cols_allowed:
118
- if col in const_df.columns and pd.api.types.is_numeric_dtype(const_df[col]):
119
- df_metric = const_df.copy()
120
- df_metric[col] = pd.to_numeric(df_metric[col], errors="coerce")
121
- df_metric = df_metric.sort_values(col, ascending=False)
122
-
123
- show_cols = ["symbol", col] if "symbol" in df_metric.columns else [col]
124
- metric_tables += f"""
125
- <div class="small-table">
126
- <div class="st-title">{col}</div>
127
- <div class="st-body">
128
- {df_to_html_color(df_metric[show_cols], metric_col=col)}
129
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  </div>
131
- """
132
-
133
- # ================= FINAL HTML =================
134
- html = f"""
135
- <!DOCTYPE html>
136
- <html>
137
- <head>
138
- <meta charset="UTF-8">
139
- <style>
140
- body {{ font-family: Arial; margin: 12px; background: #f5f5f5; font-size: 14px; }}
141
- h2, h3 {{ margin: 10px 0; }}
142
- table {{ border-collapse: collapse; width: 100%; }}
143
- th, td {{ border: 1px solid #bbb; padding: 6px; font-size: 13px; }}
144
- th {{ background: #333; color: #fff; }}
145
-
146
- .compact-table td.numeric-positive {{ color: green; font-weight: bold; }}
147
- .compact-table td.numeric-negative {{ color: red; font-weight: bold; }}
148
- .compact-table td.top-up {{ background: #b6f2b6; }}
149
- .compact-table td.top-down {{ background: #f2b6b6; }}
150
-
151
- .grid {{ display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; }}
152
- .small-table {{ background: #fff; padding: 8px; border-radius: 6px; border: 1px solid #ddd; }}
153
- .st-title {{ text-align: center; font-weight: bold; background: #222; color: #fff; padding: 6px; border-radius: 4px; }}
154
- .st-body {{ max-height: 300px; overflow-y: auto; }}
155
-
156
- .mini-card-container {{ display: flex; flex-wrap: wrap; gap: 10px; }}
157
- .mini-card {{ background: #fff; padding: 8px 10px; border-radius: 6px; border: 1px solid #ddd; min-width: 120px; }}
158
- .card-key {{ font-weight: bold; }}
159
- </style>
160
- </head>
161
-
162
- <body>
163
-
164
- <h2>Pre-Open Market {key}</h2>
165
-
166
- <h3>Info</h3>
167
- {info_cards_html}
168
-
169
- <h3>Constituents</h3>
170
- {cons_html}
171
-
172
- <h3>Key Metrics</h3>
173
- <div class="grid">
174
- {metric_tables}
175
- </div>
176
-
177
- </body>
178
- </html>
179
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  # ================= SAVE CACHE =================
182
  save(cache_key, {
183
  "date": today,
184
- "html": html
185
  })
186
 
187
- return html
 
1
+ import json
2
  import pandas as pd
3
+ from nsepython import *
4
+ import html
5
  from datetime import datetime as dt
6
+ from collections import defaultdict
7
 
8
+ # persist helpers (already exist in your project)
9
  from persist import exists, load, save
10
 
11
 
12
+ def build_indices_html():
13
  """
14
+ Generate HTML:
15
+ - main table
16
+ - dates table
17
+ - tables for all categories
18
+ - charts ONLY for key == "INDICES ELIGIBLE IN DERIVATIVES"
19
+ - flexible chart layout (no grid, auto-fit)
20
+ - DAILY CACHE ENABLED
21
  """
22
 
23
  # ================= CACHE =================
24
  today = dt.now().strftime("%Y-%m-%d")
25
+ cache_key = "indices_html"
26
 
27
  if exists(cache_key):
28
  cached = load(cache_key)
 
30
  return cached.get("html")
31
 
32
  # ================= FETCH DATA =================
33
+ p = indices() # your existing function
34
+ data_df = p.get("data", pd.DataFrame())
35
+ dates_df = p.get("dates", pd.DataFrame())
36
+
37
+ records = data_df.to_dict(orient="records") if not data_df.empty else []
38
+
39
+ # Columns to hide in category tables
40
+ hidden_cols = {
41
+ "key","chartTodayPath","chart30dPath","chart30Path","chart365dPath",
42
+ "date365dAgo","date30dAgo","previousDay","oneWeekAgo","oneMonthAgoVal",
43
+ "oneWeekAgoVal","oneYearAgoVal","index","indicativeClose"
44
+ }
45
+
46
+ # ----------- BASIC TABLE BUILDER -----------
47
+ def build_table_from_records(recs, cols=None):
48
+ if not recs:
49
+ return "<p>No data available.</p>"
50
+
51
+ if cols is None:
52
+ cols = []
53
+ for r in recs:
54
+ for k in r.keys():
55
+ if k not in cols:
56
+ cols.append(k)
57
+
58
+ header = "".join(f"<th>{html.escape(str(c))}</th>" for c in cols)
59
+
60
+ body_rows = []
61
+ for r in recs:
62
+ tds = []
63
+ for c in cols:
64
+ v = r.get(c, "")
65
+ if isinstance(v, (list, dict)):
66
+ v = str(v)
67
+ tds.append(f"<td>{html.escape('' if v is None else str(v))}</td>")
68
+ body_rows.append("<tr>" + "".join(tds) + "</tr>")
69
+
70
+ return f"""
71
+ <table>
72
+ <thead><tr>{header}</tr></thead>
73
+ <tbody>{''.join(body_rows)}</tbody>
74
+ </table>
75
+ """
76
+
77
+ # ----------- FLEXIBLE CHART BLOCK -----------
78
+ def build_chart_grid_for_record(r):
79
+
80
+ def iframe_if_exists(src, label):
81
+ if src and isinstance(src, str) and src.strip():
82
+ return f"""
83
+ <div class="chart-flex-item">
84
+ <iframe src="{html.escape(src)}" loading="lazy"
85
+ frameborder="0" title="{html.escape(label)}"></iframe>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  </div>
87
+ """
88
+ return ""
89
+
90
+ today_src = r.get("chartTodayPath") or r.get("chartToday") or ""
91
+ month30_src = r.get("chart30dPath") or r.get("chart30Path") or ""
92
+ year365_src = r.get("chart365dPath") or r.get("chart365") or ""
93
+
94
+ block = (
95
+ iframe_if_exists(today_src, "Today Chart") +
96
+ iframe_if_exists(month30_src, "30d Chart") +
97
+ iframe_if_exists(year365_src, "365d Chart")
98
+ )
99
+
100
+ if not block.strip():
101
+ return ""
102
+
103
+ title = r.get("index") or r.get("indexSymbol") or r.get("symbol") or ""
104
+
105
+ return f"""
106
+ <div class="chart-flex-block">
107
+ <div class="chart-title"><strong>{html.escape(str(title))}</strong></div>
108
+ <div class="chart-flex-container">
109
+ {block}
110
  </div>
111
+ </div>
112
+ """
113
+
114
+ # ----------- MAIN TABLE -----------
115
+ main_table_html = build_table_from_records(records)
116
+
117
+ # ----------- DATES TABLE -----------
118
+ dates_table_html = ""
119
+ if not dates_df.empty:
120
+ dates_table_html = build_table_from_records(
121
+ dates_df.to_dict(orient="records")
122
+ )
123
+
124
+ # ----------- GROUP BY KEY ----------
125
+ groups = defaultdict(list)
126
+ for r in records:
127
+ groups[r.get("key") or "UNCLASSIFIED"].append(r)
128
+
129
+ per_key_sections = []
130
+
131
+ # ----------- PER CATEGORY SECTIONS ----------
132
+ for key_name, recs in groups.items():
133
+
134
+ first = recs[0]
135
+ cols = [c for c in first.keys() if c not in hidden_cols]
136
+
137
+ preferred = ["indexSymbol", "index", "symbol", "name"]
138
+ ordered = [c for c in preferred if c in cols] + [c for c in cols if c not in preferred]
139
+
140
+ table_html = build_table_from_records(recs, ordered)
141
+
142
+ # Charts only for derivative-eligible indices
143
+ if str(key_name).strip().upper() == "INDICES ELIGIBLE IN DERIVATIVES":
144
+ charts_html = "\n".join(build_chart_grid_for_record(r) for r in recs)
145
+ else:
146
+ charts_html = ""
147
+
148
+ per_key_sections.append(f"""
149
+ <section class="key-section">
150
+ <h3>Category: {html.escape(str(key_name))} (Total: {len(recs)})</h3>
151
+ <div class="key-table">{table_html}</div>
152
+ {charts_html}
153
+ </section>
154
+ """)
155
+
156
+ # ----------- CSS -----------
157
+ css = """
158
+ <style>
159
+ body { font-family: Arial; padding: 16px; background: #fff; color: #111; }
160
+ table { border-collapse: collapse; width: 100%; margin-bottom: 14px; }
161
+ th, td { border: 1px solid #ccc; padding: 6px 8px; font-size: 13px; }
162
+ th { background: #007bff; color: white; position: sticky; top: 0; }
163
+
164
+ .scroll { max-height: 420px; overflow: auto; padding: 6px; background: #fafafa;
165
+ margin-bottom: 16px; border: 1px solid #ddd; }
166
+
167
+ .key-section {
168
+ border: 1px solid #e6eef6;
169
+ background: #fbfeff;
170
+ border-radius: 6px;
171
+ padding: 10px;
172
+ margin-bottom: 30px;
173
+ }
174
+
175
+ .chart-flex-block {
176
+ border: 1px solid #ddd;
177
+ background: #fff;
178
+ padding: 8px;
179
+ border-radius: 6px;
180
+ margin-bottom: 14px;
181
+ }
182
+
183
+ .chart-title { margin-bottom: 6px; font-size: 14px; }
184
+
185
+ .chart-flex-container {
186
+ display: flex;
187
+ flex-wrap: wrap;
188
+ gap: 10px;
189
+ }
190
+
191
+ .chart-flex-item {
192
+ flex: 1 1 300px;
193
+ min-height: 180px;
194
+ border: 1px solid #ccc;
195
+ border-radius: 6px;
196
+ overflow: hidden;
197
+ }
198
+
199
+ .chart-flex-item iframe {
200
+ width: 100%;
201
+ height: 100%;
202
+ border: 0;
203
+ }
204
+ </style>
205
+ """
206
+
207
+ # ----------- FINAL HTML -----------
208
+ html_out = "\n".join([
209
+ "<!DOCTYPE html>",
210
+ "<html><head><meta charset='utf-8'><title>NSE Indices</title>",
211
+ css,
212
+ "</head><body>",
213
+ "<h1>NSE Indices — Full Static Render</h1>",
214
+ f"<div class='meta'>Generated: {html.escape(dt.now().strftime('%Y-%m-%d %H:%M:%S'))}</div>",
215
+ "<h2>Main Indices Table</h2>",
216
+ "<div class='scroll'>", main_table_html, "</div>",
217
+ "<h2>Dates / Meta</h2>" if dates_table_html else "",
218
+ "<div class='scroll'>" if dates_table_html else "",
219
+ dates_table_html,
220
+ "</div>" if dates_table_html else "",
221
+ "<h2>Categories</h2>",
222
+ *per_key_sections,
223
+ "</body></html>"
224
+ ])
225
 
226
  # ================= SAVE CACHE =================
227
  save(cache_key, {
228
  "date": today,
229
+ "html": html_out
230
  })
231
 
232
+ return html_out