eshan6704 commited on
Commit
03533f6
·
verified ·
1 Parent(s): f09ef01

Update preopen_html.py

Browse files
Files changed (1) hide show
  1. preopen_html.py +152 -206
preopen_html.py CHANGED
@@ -1,28 +1,22 @@
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,203 +24,155 @@ def build_indices_html():
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
 
 
 
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
  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
+ .compact-table td.numeric-positive {{ color: green; font-weight: bold; }}
146
+ .compact-table td.numeric-negative {{ color: red; font-weight: bold; }}
147
+ .compact-table td.top-up {{ background: #b6f2b6; }}
148
+ .compact-table td.top-down {{ background: #f2b6b6; }}
149
+ .grid {{ display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; }}
150
+ .small-table {{ background: #fff; padding: 8px; border-radius: 6px; border: 1px solid #ddd; }}
151
+ .st-title {{ text-align: center; font-weight: bold; background: #222; color: #fff; padding: 6px; border-radius: 4px; }}
152
+ .st-body {{ max-height: 300px; overflow-y: auto; }}
153
+ .mini-card-container {{ display: flex; flex-wrap: wrap; gap: 10px; }}
154
+ .mini-card {{ background: #fff; padding: 8px 10px; border-radius: 6px; border: 1px solid #ddd; min-width: 120px; }}
155
+ .card-key {{ font-weight: bold; }}
156
+ </style>
157
+ </head>
158
+ <body>
159
+ <h2>Pre-Open Market — {key}</h2>
160
+ <h3>Info</h3>
161
+ {info_cards_html}
162
+ <h3>Constituents</h3>
163
+ {cons_html}
164
+ <h3>Key Metrics</h3>
165
+ <div class="grid">
166
+ {metric_tables}
167
+ </div>
168
+ </body>
169
+ </html>
170
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
  # ================= SAVE CACHE =================
173
  save(cache_key, {
174
  "date": today,
175
+ "html": html
176
  })
177
 
178
+ return html