@@ -19,61 +19,124 @@ def create_master_dashboard(export_data, output_dir, log):
1919 """Helper to inject the master aggregated JSON into the dashboard template."""
2020 import base64
2121 import json
22+ import re
23+ from pathlib import Path
2224
2325 assets_dir = Path (__file__ ).resolve ().parent / "assets"
26+ html_files = assets_dir / "dashboard"
27+ js_files = html_files / "js"
28+
29+ def _load_asset (folder , filename , default = "" ):
30+ p = folder / filename
31+ return p .read_text (encoding = "utf-8" ) if p .exists () else default
2432
2533 # Safely load local Frameworks or fallback to CDNs
26- vis_local = assets_dir / "vis-network.min.js"
34+ vis_local = js_files / "vis-network.min.js"
2735 if vis_local .exists ():
2836 vis_injection = f'<script>\n { vis_local .read_text (encoding = "utf-8" )} \n </script>'
2937 else :
3038 vis_injection = '<script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>'
3139
32- mol3d_local = assets_dir / "3Dmol-min.js"
40+ mol3d_local = js_files / "3Dmol-min.js"
3341 if mol3d_local .exists ():
3442 mol3d_injection = f'<script>\n { mol3d_local .read_text (encoding = "utf-8" )} \n </script>'
3543 else :
3644 mol3d_injection = '<script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>'
3745
38- mhcx_logo_path = assets_dir / "MHCXGraph logo.png"
39- mhcx_logo_injection = "<h2>MHCXGraph</h2>"
46+ fabric_local = js_files / "fabric.min.js"
47+ if fabric_local .exists ():
48+ fabric_injection = f'<script>\n { fabric_local .read_text (encoding = "utf-8" )} \n </script>'
49+ else :
50+ fabric_injection = '<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>'
51+
52+ plotly_local = js_files / "plotly.min.js"
53+ if plotly_local .exists ():
54+ plotly_injection = f'<script>\n { plotly_local .read_text (encoding = "utf-8" )} \n </script>'
55+ else :
56+ plotly_injection = '<script src="https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.32.0/plotly.min.js"></script>'
57+
58+ mhcx_logo_path = assets_dir / "images/MHCXGraph logo.png"
59+ mhcx_logo_injection = "<b>MHCXGraph</b>\n "
4060 favicon_injection = ""
4161 if mhcx_logo_path .exists ():
4262 with open (mhcx_logo_path , "rb" ) as image_file :
4363 encoded = base64 .b64encode (image_file .read ()).decode ("utf-8" )
44- mhcx_logo_injection = f'<img src="data:image/png;base64,{ encoded } " alt="MHCXGraph Logo" style="max- width: 80 %; height: auto ;">'
64+ mhcx_logo_injection = f'<img src="data:image/png;base64,{ encoded } " alt="MHCXGraph Logo" style="width: 100 %; height: 100%; margin-bottom: -10px ;">'
4565 favicon_injection = f'<link rel="icon" type="image/png" href="data:image/png;base64,{ encoded } ">'
4666
47- logo_dark_path = assets_dir / "LNBio white.png"
48- logo_light_path = assets_dir / "LNBio.png"
67+ logo_dark_path = assets_dir / "images/ LNBio white.png"
68+ logo_light_path = assets_dir / "images/ LNBio.png"
4969 logo_injection = ""
5070 if logo_light_path .exists ():
5171 with open (logo_light_path , "rb" ) as image_file :
5272 encoded = base64 .b64encode (image_file .read ()).decode ("utf-8" )
53- logo_injection += f'<img src="data:image/png;base64,{ encoded } " alt="LNBio Logo" class="logo-light" style="height: 7rem ; width: auto;">'
73+ logo_injection += f'<img src="data:image/png;base64,{ encoded } " alt="LNBio Logo" class="logo-light" style="height: 8rem ; width: auto;">'
5474 if logo_dark_path .exists ():
5575 with open (logo_dark_path , "rb" ) as image_file :
5676 encoded = base64 .b64encode (image_file .read ()).decode ("utf-8" )
57- logo_injection += f'<img src="data:image/png;base64,{ encoded } " alt="LNBio Logo" class="logo-dark" style="height: 7rem ; width: auto;">'
77+ logo_injection += f'<img src="data:image/png;base64,{ encoded } " alt="LNBio Logo" class="logo-dark" style="height: 8rem ; width: auto;">'
5878 if not logo_injection :
59- log .debug ("LNBio logos not found in assets/. Skipping logo injection." )
60-
79+ log .debug ("LNBio logos not found in assets/images. Skipping logo injection." )
6180
62- template_path = assets_dir / "dashboard_template.html"
63- try :
64- with open (template_path , "r" , encoding = "utf-8" ) as f :
65- html_template = f .read ()
66- except FileNotFoundError :
67- log .error (f"Template not found at { template_path } ." )
81+ # --- NEW MODULAR LOADING LOGIC ---
82+ html_template = _load_asset (html_files , "base.html" )
83+ if not html_template :
84+ log .error (f"Template base.html not found at { html_files } ." )
6885 return
6986
70- final_html = html_template .replace ("__GRAPH_DATA_INJECTION__" , json .dumps (export_data ))
71- final_html = final_html .replace ("__FAVICON_INJECTION__" , favicon_injection )
72- final_html = final_html .replace ("__VIS_JS_INJECTION__" , vis_injection )
73- final_html = final_html .replace ("__3DMOL_JS_INJECTION__" , mol3d_injection )
87+ sidebar_html = _load_asset (html_files , "sidebar.html" )
88+ modal_html = _load_asset (html_files , "export_modal.html" )
89+ main_js = _load_asset (js_files , "main.js" )
90+ modal_js = _load_asset (js_files , "export_modal.js" )
91+ grid_js = _load_asset (js_files , "grid.js" )
92+ data_js = _load_asset (js_files , "data.js" )
93+ theme_js = _load_asset (js_files , "theme.js" )
94+ viewer_js = _load_asset (js_files , "viewer.js" )
95+ structures_js = _load_asset (js_files , "structures.js" )
96+ init_js = _load_asset (js_files , "init_functions.js" )
97+ analysis_js = _load_asset (js_files , "analysis.js" )
98+ graph_js = _load_asset (js_files , "graph.js" )
99+
100+ def split_html (raw_html ):
101+ css_match = re .search (r'<style>(.*?)</style>' , raw_html , re .DOTALL )
102+ css = css_match .group (1 ) if css_match else ""
103+ clean_html = re .sub (r'<style>.*?</style>' , '' , raw_html , flags = re .DOTALL ).strip ()
104+ return css , clean_html
105+
106+ def inject_js (html , name , code ):
107+ placeholder = f"__{ name .upper ()} _JS_INJECTION__"
108+ return html .replace (placeholder , code )
109+
110+ sidebar_css , sidebar_dom = split_html (sidebar_html )
111+ modal_css , modal_dom = split_html (modal_html )
112+
113+ # Inject everything
114+ final_html = html_template .replace ("__FAVICON_INJECTION__" , favicon_injection )
115+ final_html = inject_js (final_html , "vis" , vis_injection )
116+ final_html = inject_js (final_html , "3Dmol" , mol3d_injection )
117+ final_html = inject_js (final_html , "fabric" , fabric_injection ) # ADD THIS
118+ final_html = inject_js (final_html , "plotly" , plotly_injection )
119+ final_html = final_html .replace ("__SIDEBAR_CSS_INJECTION__" , sidebar_css )
120+ final_html = final_html .replace ("__MODAL_CSS_INJECTION__" , modal_css )
121+ final_html = final_html .replace ("__SIDEBAR_HTML_INJECTION__" , sidebar_dom )
74122 final_html = final_html .replace ("__MHCXGRAPH_LOGO_INJECTION__" , mhcx_logo_injection )
75123 final_html = final_html .replace ("__LNBIO_LOGO_INJECTION__" , logo_injection )
76124
125+ final_html = final_html .replace ("__MODAL_HTML_INJECTION__" , modal_dom )
126+
127+ # Javascript injection
128+ final_html = inject_js (final_html , "main" , main_js )
129+ final_html = inject_js (final_html , "data" , data_js )
130+ final_html = inject_js (final_html , "graph_data" , json .dumps (export_data ))
131+ final_html = inject_js (final_html , "init" , init_js )
132+ final_html = inject_js (final_html , "viewer" , viewer_js )
133+ final_html = inject_js (final_html , "theme" , theme_js )
134+ final_html = inject_js (final_html , "grid" , grid_js )
135+ final_html = inject_js (final_html , "analysis" , analysis_js )
136+ final_html = inject_js (final_html , "graph" , graph_js )
137+ final_html = inject_js (final_html , "structures" , structures_js )
138+ final_html = inject_js (final_html , "modal" , modal_js )
139+
77140 # Dynamically name the output file based on the mode
78141 mode = export_data .get ("mode" , "all" )
79142 file_name = "Dashboard_Pairs.html" if mode == "pair" else "Dashboard_All.html"
0 commit comments