eshan6704 commited on
Commit
d25a918
·
verified ·
1 Parent(s): 36176df

Update build_index_live_html.py

Browse files
Files changed (1) hide show
  1. build_index_live_html.py +284 -0
build_index_live_html.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from nsepython import *
2
  import pandas as pd
3
 
@@ -240,6 +241,289 @@ th {{
240
  {metric_tables}
241
  </div>
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  </body>
244
  </html>
245
  """
 
1
+ '''
2
  from nsepython import *
3
  import pandas as pd
4
 
 
241
  {metric_tables}
242
  </div>
243
 
244
+ </body>
245
+ </html>
246
+ """
247
+ return html
248
+ '''
249
+ from nsepython import *
250
+ import pandas as pd
251
+
252
+ def build_index_live_html(name=""):
253
+ p = nse_index_live(name)
254
+
255
+ full_df = p.get("data", pd.DataFrame())
256
+ rem_df = p.get("rem", pd.DataFrame())
257
+
258
+ if full_df.empty:
259
+ main_df = pd.DataFrame()
260
+ const_df = pd.DataFrame()
261
+ else:
262
+ main_df = full_df.iloc[[0]]
263
+ const_df = full_df.iloc[1:] # Constituents
264
+ if not const_df.empty:
265
+ const_df = const_df.iloc[:, 1:] # Remove first column
266
+
267
+ # Columns to move from constituents to info
268
+ move_to_info = [c for c in ['segment', 'equityTime', 'preOpenTime'] if c in const_df.columns]
269
+ if move_to_info:
270
+ rem_df = pd.concat([rem_df, const_df[move_to_info].iloc[[0]]], axis=1)
271
+ const_df = const_df.drop(columns=move_to_info)
272
+
273
+ # Drop unnecessary columns from Constituents
274
+ drop_cols_const = [
275
+ "identifier", "ffmc", "stockIndClosePrice", "lastUpdateTime",
276
+ "chartTodayPath", "chart30dPath", "chart365dPath", "series",
277
+ "symbol_meta", "activeSeries", "debtSeries", "isFNOSec",
278
+ "isCASec", "isSLBSec", "isDebtSec", "isSuspended",
279
+ "tempSuspendedSeries", "isETFSec", "isDelisted",
280
+ "slb_isin", "isMunicipalBond", "isHybridSymbol", "QuotePreOpenFlag"
281
+ ]
282
+ const_df = const_df.drop(columns=[c for c in drop_cols_const if c in const_df.columns])
283
+
284
+ # Drop unnecessary columns from Main Data
285
+ drop_cols_main = [
286
+ "series", "symbol_meta", "companyName", "industry", "activeSeries", "debtSeries",
287
+ "isFNOSec", "isCASec", "isSLBSec", "isDebtSec", "isSuspended", "tempSuspendedSeries",
288
+ "isETFSec", "isDelisted", "isin", "slb_isin", "listingDate", "isMunicipalBond",
289
+ "isHybridSymbol", "segment", "equityTime", "preOpenTime", "QuotePreOpenFlag"
290
+ ]
291
+ main_df = main_df.drop(columns=[c for c in drop_cols_main if c in main_df.columns])
292
+
293
+ # Ensure pChange is numeric and sort
294
+ if 'pChange' in const_df.columns:
295
+ const_df['pChange'] = pd.to_numeric(const_df['pChange'], errors='coerce')
296
+ const_df = const_df.sort_values('pChange', ascending=False)
297
+
298
+ # ================= HELPER FUNCTION: COLOR-CODE AND FORMAT NUMERIC =================
299
+ def df_to_html_color(df, metric_col=None):
300
+ df_html = df.copy()
301
+ top3_up = []
302
+ top3_down = []
303
+ if metric_col and metric_col in df_html.columns and pd.api.types.is_numeric_dtype(df_html[metric_col]):
304
+ col_numeric = df_html[metric_col].dropna()
305
+ top3_up = col_numeric.nlargest(3).index.tolist()
306
+ top3_down = col_numeric.nsmallest(3).index.tolist()
307
+
308
+ for idx, row in df_html.iterrows():
309
+ for col in df_html.columns:
310
+ val = row[col]
311
+ style = ""
312
+ if pd.api.types.is_numeric_dtype(type(val)) or isinstance(val, (int, float)):
313
+ val_fmt = f"{val:.2f}"
314
+ if val > 0:
315
+ style = "numeric-positive"
316
+ elif val < 0:
317
+ style = "numeric-negative"
318
+ if metric_col and col == metric_col:
319
+ if idx in top3_up:
320
+ style += " top-up"
321
+ elif idx in top3_down:
322
+ style += " top-down"
323
+ df_html.at[idx, col] = f'<span class="{style.strip()}">{val_fmt}</span>'
324
+ else:
325
+ df_html.at[idx, col] = str(val)
326
+ return df_html.to_html(index=False, escape=False, classes="compact-table")
327
+
328
+ # ================= MERGE INFO AND MAIN KEYS INTO MINI CARDS =================
329
+ def merge_info_main_cards(rem_df, main_df):
330
+ combined = pd.concat([rem_df, main_df], axis=1)
331
+ # Remove duplicate columns
332
+ combined = combined.loc[:, ~combined.columns.duplicated()]
333
+ # Generate mini cards
334
+ cards_html = '<div class="mini-card-container">'
335
+ for col in combined.columns:
336
+ val = combined.at[0, col] if not combined.empty else ""
337
+ cards_html += f'''
338
+ <div class="mini-card">
339
+ <div class="card-key">{col}</div>
340
+ <div class="card-val">{val}</div>
341
+ </div>
342
+ '''
343
+ cards_html += '</div>'
344
+ return cards_html
345
+
346
+ info_cards_html = merge_info_main_cards(rem_df, main_df)
347
+
348
+ cons_html = df_to_html_color(const_df)
349
+
350
+ # ================= METRIC TABLES =================
351
+ metric_cols = [
352
+ "pChange", "totalTradedValue", "nearWKH", "nearWKL",
353
+ "perChange365d", "perChange30d"
354
+ ]
355
+
356
+ metric_tables = ""
357
+ for col in metric_cols:
358
+ if col not in const_df.columns:
359
+ continue
360
+
361
+ df_const = const_df.copy()
362
+ df_const[col] = pd.to_numeric(df_const[col], errors="ignore")
363
+ df_const = df_const.sort_values(col, ascending=False)
364
+ df_html = df_to_html_color(df_const[['symbol', col]], metric_col=col)
365
+
366
+ metric_tables += f"""
367
+ <div class="small-table">
368
+ <div class="st-title">{col}</div>
369
+ <div class="st-body">{df_html}</div>
370
+ </div>
371
+ """
372
+
373
+ # ================= FINAL HTML =================
374
+ html = f"""
375
+ <!DOCTYPE html>
376
+ <html>
377
+ <head>
378
+ <meta charset="UTF-8">
379
+
380
+ <style>
381
+ body {{
382
+ font-family: Arial;
383
+ margin: 12px;
384
+ background: #f5f5f5;
385
+ color: #222;
386
+ font-size: 14px;
387
+ }}
388
+
389
+ h2, h3 {{
390
+ margin: 12px 0 6px 0;
391
+ font-weight: 600;
392
+ }}
393
+
394
+ table {{
395
+ border-collapse: collapse;
396
+ width: 100%;
397
+ table-layout: auto;
398
+ }}
399
+
400
+ th, td {{
401
+ border: 1px solid #bbb;
402
+ padding: 5px 8px;
403
+ text-align: left;
404
+ font-size: 13px;
405
+ }}
406
+
407
+ th {{
408
+ background: #333;
409
+ color: white;
410
+ font-weight: 600;
411
+ }}
412
+
413
+ .compact-table td.numeric-positive {{
414
+ color: green;
415
+ font-weight: bold;
416
+ }}
417
+ .compact-table td.numeric-negative {{
418
+ color: red;
419
+ font-weight: bold;
420
+ }}
421
+
422
+ /* Highlight top 3 gainers / losers */
423
+ .compact-table td.top-up {{
424
+ background: #a8f0a5; /* light green */
425
+ }}
426
+ .compact-table td.top-down {{
427
+ background: #f0a8a8; /* light red */
428
+ }}
429
+
430
+ /* Fixed row height & clipping for Constituent Table */
431
+ #constituents-table tr, #constituents-table td {{
432
+ max-height: 25px;
433
+ height: 25px;
434
+ overflow: hidden;
435
+ white-space: nowrap;
436
+ text-overflow: ellipsis;
437
+ }}
438
+
439
+ .small-table {{
440
+ background: white;
441
+ border-radius: 6px;
442
+ padding: 8px;
443
+ box-shadow: 0px 1px 4px rgba(0,0,0,0.15);
444
+ border: 1px solid #ddd;
445
+ overflow-y: auto;
446
+ }}
447
+
448
+ .st-title {{
449
+ font-size: 14px;
450
+ text-align: center;
451
+ margin-bottom: 6px;
452
+ font-weight: bold;
453
+ background: #222;
454
+ color: white;
455
+ padding: 5px 0;
456
+ border-radius: 4px;
457
+ }}
458
+
459
+ .st-body {{
460
+ max-height: 300px; /* vertical scroll for metric tables */
461
+ overflow-y: auto;
462
+ font-size: 12px;
463
+ }}
464
+
465
+ .compact-section {{
466
+ background: white;
467
+ padding: 8px;
468
+ border-radius: 6px;
469
+ box-shadow: 0 1px 4px rgba(0,0,0,0.12);
470
+ border: 1px solid #ddd;
471
+ margin-bottom: 15px;
472
+ overflow-x: visible;
473
+ }}
474
+
475
+ .grid {{
476
+ display: grid;
477
+ grid-template-columns: repeat(5, 1fr);
478
+ gap: 12px;
479
+ margin-top: 12px;
480
+ }}
481
+
482
+ /* Mini cards for info + main */
483
+ .mini-card-container {{
484
+ display: flex;
485
+ flex-wrap: wrap;
486
+ gap: 10px;
487
+ }}
488
+ .mini-card {{
489
+ background: #fff;
490
+ padding: 8px 10px;
491
+ border-radius: 6px;
492
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12);
493
+ min-width: 120px;
494
+ font-size: 13px;
495
+ }}
496
+ .card-key {{
497
+ font-weight: bold;
498
+ color: #333;
499
+ margin-bottom: 2px;
500
+ }}
501
+ .card-val {{
502
+ color: #222;
503
+ }}
504
+ </style>
505
+ </head>
506
+ <body>
507
+
508
+ <h2>Live Index Data: {name or 'Default Index'}</h2>
509
+
510
+ <div class="compact-section">
511
+ <h3>Index Info + Main Data</h3>
512
+ {info_cards_html}
513
+ </div>
514
+
515
+ <div class="compact-section">
516
+ <h3>Constituents</h3>
517
+ <div id="constituents-table">
518
+ {cons_html}
519
+ </div>
520
+ </div>
521
+
522
+ <h3>Metric Tables (All Symbols)</h3>
523
+ <div class="grid">
524
+ {metric_tables}
525
+ </div>
526
+
527
  </body>
528
  </html>
529
  """