Skip to content

Commit fa09b42

Browse files
authored
Merge pull request #44977 from AdrianoDee/fix_RelMon_piecharts
Fix RelMon Piecharts using `Chart.js`
2 parents 10c5256 + a2e21c5 commit fa09b42

File tree

3 files changed

+143
-83
lines changed

3 files changed

+143
-83
lines changed

Utilities/RelMon/python/definitions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,11 @@
197197
("!(mu20+|wzMu20+|jet20+)","Btag@1"))
198198
data_pattern_blist_pairs=()
199199

200+
## colors for gauge
201+
from matplotlib.colors import LinearSegmentedColormap
202+
gauge_cmap=LinearSegmentedColormap.from_list('rg',["r", "orange","y","lime"], N=256)
203+
204+
## cms logo
205+
cms_logo_url = "https://cms-docdb.cern.ch/cgi-bin/PublicDocDB/RetrieveFile?docid=3045&filename=CMSlogo_color_label_1024_May2014.png&version=3"
206+
207+

Utilities/RelMon/python/directories2html.py

Lines changed: 98 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import os
2626
import hashlib
27+
import random
28+
from matplotlib.colors import to_hex
2729

2830
if "RELMON_SA" in os.environ:
2931
from .dirstructure import Comparison,Directory
@@ -81,9 +83,12 @@ def fairy_url_single(run,sample,version,plot_path,tier,draw_opts="",h=250,w=200)
8183
def get_page_header(directory=None, standalone=False, additional_header=""):
8284
style_location="/cms-service-reldqm"
8385
if standalone:
84-
style_location = "http://cms-service-reldqm.web.cern.ch/" + style_location +"/"
86+
style_location = "https://raw.githubusercontent.com/cms-PdmV/RelMonService2/77c534ec93401ca5de222ac62a6422f02389dafc/report_website/" #RelMonService2
8587
javascripts=''
8688
style=''
89+
tablestyle=''
90+
thead_h = 400
91+
wrapper_h = 1500
8792
if directory!=None and len(directory.comparisons)>0:
8893
meta=directory.meta
8994
style='img.fail {border:1px solid #ff0000;}\n'+\
@@ -94,18 +99,34 @@ def get_page_header(directory=None, standalone=False, additional_header=""):
9499
'a.black_link:hover {color: #737373}\n'+\
95100
'a.black_link:visited {color: #333333}\n'+\
96101
'a.black_link:active {color: #333333}\n'
102+
## fixed first row and first column table
103+
104+
wrapper_h = min(thead_h + (70 * len(directory.comparisons)),1800)
105+
106+
tablestyle = '\n.wrapper { overflow: auto; height: %dpx;} \n'%(wrapper_h)
107+
tablestyle += 'table { position: relative; border-collapse: separate; border-spacing: 0;} \n'
108+
tablestyle += 'table { position: relative; border-collapse: separate; border-spacing: 0;} \n'
109+
tablestyle += 'table th, table td { width: 50px; padding: 5px; background-color: white;} \n'
110+
tablestyle += 'table th { position: sticky; top: 0; z-index: 2; height: %dpx;} \n'%(thead_h)
111+
tablestyle += 'table th:nth-child(1) { left:0; z-index:3;} \n'
112+
tablestyle += '.sticky-col { position: sticky; background-color: #C9FFD1 ; width: 200px; left:0}\n'
113+
tablestyle += '.center_head { position: absolute; top: 50%; left: 50%;} \n'
114+
tablestyle += '.vertical_head {top: 60%; -webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg); -moz-transform: translateX(-50%) translateY(-50%) rotate(-90deg);} \n'
97115
javascripts=""
116+
98117

99-
118+
100119
html='<html>'+\
101120
'<head>'+\
102121
'<title>RelMon Summary</title>'+\
103-
'<link rel="stylesheet" href="%s/style/blueprint/screen.css" type="text/css" media="screen, projection">'%style_location+\
104-
'<link rel="stylesheet" href="%s/style/blueprint/print.css" type="text/css" media="print">'%style_location+\
105-
'<link rel="stylesheet" href="%s/style/blueprint/plugins/fancy-type/screen.css" type="text/css" media="screen, projection">'%style_location+\
122+
'<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>' + \
123+
'<link rel="stylesheet" href="%s/screen.css" type="text/css" media="screen, projection">'%style_location+\
124+
'<link rel="stylesheet" href="%s/print.css" type="text/css" media="print">'%style_location+\
125+
'<link rel="stylesheet" href="%s/fancy-type-screen.css" type="text/css" media="screen, projection">'%style_location+\
106126
'<style type="text/css">'+\
107127
'.rotation {display: block;-webkit-transform: rotate(-90deg);-moz-transform: rotate(-90deg); }'+\
108128
'%s'%style+\
129+
'%s'%tablestyle +\
109130
'</style>'+\
110131
'%s'%javascripts+\
111132
'%s'%additional_header+\
@@ -123,10 +144,7 @@ def get_page_footer():
123144
#-------------------------------------------------------------------------------
124145

125146
def get_title_section(directory, hashing_flag, standalone, depth=2):
126-
if standalone:
127-
cms_logo_url = "http://cms-service-reldqm.web.cern.ch/cms-service-reldqm/style/CMS.gif"
128-
else:
129-
cms_logo_url = "cms-service-reldqm/style/CMS.gif"
147+
130148
mother_name=basename(directory.mother_dir)
131149
mother_file_name=""
132150
if depth==1:
@@ -212,7 +230,7 @@ def get_subdirs_section(directory, hashing_flag):
212230
html+='</div>'
213231

214232
html+='<div class="span-6 last">'
215-
html+='<a href="%s"><img src="%s" class="top right"></a>'%(link,subdir.get_summary_chart_ajax(150,100))
233+
html+= subdir.get_piechart_js(120,link)
216234
html+='</div>'
217235

218236
html+='<hr>'
@@ -263,7 +281,7 @@ def get_summary_section(directory,matrix_page=True):
263281
html+='</div>'
264282

265283
html+='<div class="span-7 colborder">'+\
266-
'<img src="%s" class="top right">'%directory.get_summary_chart_ajax(200,200)+\
284+
'%s'%(directory.get_piechart_js(150)) +\
267285
'</div>'+\
268286
'<div class="span-9 last">'
269287
if matrix_page:
@@ -491,27 +509,35 @@ def directory2html(directory, hashing, standalone, depth=0):
491509

492510
#chdir(old_cwd)
493511

494-
#-------------------------------------------------------------------------------
512+
def build_gauge_js(rate,w=100,minrate=.80,add_rate=False):
513+
514+
color = to_hex(gauge_cmap((rate-minrate)/(1.0-minrate)))
515+
font_size = int(w/9)
516+
gauge_max = 1. - rate
517+
518+
name = random.getrandbits(64) # just a random has for the canvas
519+
html = ""
520+
html += '<canvas id="%s" style="width:100%%;max-width:%d"></canvas>'%(name,w)
521+
522+
# "gauge" chart
523+
html += '<script> new Chart("%s",'%(name)
524+
html += '{ type: "doughnut",'
495525

496-
def build_gauge(total_success_rate,minrate=.80,small=False,escaped=False):
497-
total_success_rate_scaled=(total_success_rate-minrate)
498-
total_success_rate_scaled_repr=total_success_rate_scaled/(1-minrate)
499-
if total_success_rate_scaled_repr<0:
500-
total_success_rate_scaled_repr=0
501-
size_s="200x100"
502-
if small:
503-
size_s="40x30"
504-
#print "Total success rate %2.2f and scaled %2.2f "%(total_success_rate,total_success_rate_scaled)
505-
gauge_link ="https://chart.googleapis.com/chart?chs=%s&cht=gom"%size_s
506-
gauge_link+="&chd=t:%2.1f"%(total_success_rate_scaled_repr*100.)
507-
if not small:
508-
gauge_link+="&chxt=x,y&chxl=0:|%2.1f%%|1:|%i%%|%i%%|100%%"%(total_success_rate*100,minrate*100.,(1+minrate)*50)
509-
gauge_link+="&chma=10,10,10,0"
510-
img_tag= '<img src="%s">'%gauge_link
511-
if escaped:
512-
img_tag=html.escape(img_tag)
513-
return img_tag
526+
# data
527+
html += 'data: {'
528+
html += 'labels: ["Success", "Failure"],'
529+
html += 'datasets: [{ backgroundColor: ["%s", "#C7C7C7"],'%(color)
530+
html += 'data: [%f,%f]}] },'%(rate,gauge_max)
514531

532+
#options
533+
html += 'options: { responsive: true, rotation: -3.1415926536, circumference: 3.1415926536 ,' ## in radiants
534+
html += ' legend: { display: false }, tooltips: {enabled: false}, hover: {mode: null}},'
535+
html += '}); </script>'
536+
if add_rate:
537+
html += '<div style="width: 100%%; position: relative; left: %d; margin-top: -%dpx; font-align: center">'%(int(2*w/7),font_size)
538+
html += '<span style="color: %s; font-family: courier; font-size: %dpx;">%.2f%%</span></div>'%(color,font_size,rate*100.)
539+
540+
return html
515541
#-------------------------------------------------------------------------------
516542

517543
def get_aggr_pairs_info(dir_dict,the_aggr_pairs=[]):
@@ -614,7 +640,7 @@ def make_categories_summary(dir_dict,aggregation_rules):
614640
html+='</div>'
615641

616642
html+='<div class="span-6 last">'
617-
html+=build_gauge(average_success_rate)
643+
html+=build_gauge_js(average_success_rate,add_rate=True)
618644
html+='</div>'
619645

620646
html+='<hr>'
@@ -648,7 +674,7 @@ def make_twiki_table(dir_dict,aggregation_rules):
648674

649675
for cat_name,present_subdirs,total_weight,average_success_rate in aggr_pairs_info:
650676
#print cat_name,present_subdirs,total_weight,average_success_rate
651-
html+=build_gauge(average_success_rate,small=True,escaped=True)
677+
html+=build_gauge_js(average_success_rate,w=40)
652678
html+=" | "
653679

654680
html+='</div> <a href="#top">Top...</a>'
@@ -691,7 +717,7 @@ def make_barchart_summary(dir_dict,name="the_chart",title="DQM directory",the_ag
691717
});
692718
}
693719
</script>
694-
"""%(name,40*counter,title)
720+
"""%(name,35*counter,title)
695721
return script
696722

697723

@@ -802,7 +828,6 @@ def make_summary_table(indir,aggregation_rules,aggregation_rules_twiki, hashing_
802828
page_html+=make_categories_summary(dir_dict,aggregation_rules)
803829

804830
# Make the Directories chart
805-
page_html+='<div class="span-24"><h2 class="alt"><a name="detailed_barchart">Detailed Barchart</a></h2></div>'
806831
page_html+='<div id="dir_chart"></div> <a href="#top">Top...</a><hr>'
807832

808833
# Barbarian vertical space. Suggestions are welcome
@@ -811,59 +836,61 @@ def make_summary_table(indir,aggregation_rules,aggregation_rules_twiki, hashing_
811836

812837

813838
# Prepare the table
814-
page_html+='<div class="span-24"><h2 class="alt"><a name="summary_table">Summary Table</a></h2></div>'
839+
page_html+='<div class="span-24"><h2 class="alt"><a name="summary_table">Summary Table</a></h2> <h4> <span class="alt"> (scrollable) </span> </h4> </div>'
815840

816-
for i in range(5):
817-
page_html+='<div class="span-24"><p></p></div>\n'
818-
841+
div_width= min(len(dir_dict.keys()) * 70 + 500,1500) #80 px per column + 200 for the first column
842+
page_html+='<div class="wrapper" style = "width: %dpx;">'%(div_width)
819843
page_html+="""
820-
<table border="1" >
844+
<table>
845+
<thead>
821846
<tr>
822-
<td> </td>
847+
<th> <p class = "vertical_head center_head"></p> </th>
823848
"""
824849

825850
# First row with samples
826851
page_html+="""
827-
<td><div class="span-1"><p class="rotation" style="alt"><b>Summary</b></p></div></td>"""
852+
<th> <p class = "vertical_head center_head">Summary</p></th>"""
828853

829854
sorted_samples=sorted(dir_dict.keys())
830855
for sample in sorted_samples:
831-
sample_nick=sample
832-
## For runs: put only the number after the _
833-
#if "_" in sample:
834-
#run_number=sample.split("_")[-1]
835-
#if (not run_number.isalpha()) and len(run_number)>=6:
836-
#sample_nick=run_number
837-
856+
if "_" in sample and "Data" not in sample:
857+
sample_nick="_".join(sample.split("X_")[0].split("_")[:-1])
858+
# Cleaning for MC: just the fragment
859+
elif "Data" in sample and "RelVal" in sample:
860+
sample_nick = "".join([sample.split("_")[0],sample.split("RelVal")[-1]])
861+
# Cleaning for Data: PD + Era + Run
862+
else:
863+
sample_nick = sample
838864

839865
page_html+="""
840-
<td><div class="span-1"><p class="rotation" style="">%s</p></div></td>"""%sample_nick
841-
page_html+=" </tr>\n"
866+
<th> <p class = "vertical_head center_head">%s</th></p>"""%sample_nick
867+
page_html+="</tr> \n </thead> \n </tbody> \n"
842868

843869

844870
# FIRST ROW
845871
# Now the summaries at the beginning of the table
846872
page_html+="<tr>"
847-
page_html+='<td style="background-color:white;"><div class="span-1">'
873+
page_html+='<td class="sticky-col">'
848874

849-
page_html+='<b>Summary</b></div></td>'
850-
page_html+='<td style="background-color:white;" class = "colborder" ><div class="span-1"><img src="%s" alt="%s"></div></td>'%(global_dir.get_summary_chart_ajax(55,55),get_pie_tooltip(global_dir))
875+
page_html+='<b>Summary</b></td>'
876+
page_html+='<td><div class="span-1"> %s </div></td>'%(global_dir.get_piechart_js(50))
851877
for sample in sorted_samples:
852878
col=dir_dict[sample]
853879
# check if the directory was a top one or not
854880
summary_page_name="RelMonSummary.html"
855881
if col.name!="":
856882
summary_page_name=hash_name(col.name, hashing_flag)+".html"
857-
img_link=col.get_summary_chart_ajax(55,55)
858-
page_html+='<td style="background-color:white;"><div class="span-1">'
859-
page_html+='<a href="%s/%s"><img src="%s" title="%s"></a></div></td>' %(sample,summary_page_name,img_link,get_pie_tooltip(col))
883+
title = get_pie_tooltip(col)
884+
chart = col.get_piechart_js(50,sample+"/"+summary_page_name)
885+
page_html+='<td>'
886+
page_html+='%s </a></td>' %(chart)
860887
page_html+="</tr>"
861888

862889
# Now the content
863890
for subdir_name in all_subdirs:
864891

865892
page_html+=' <tr>\n'
866-
page_html+=' <td style="background-color:white;">%s</td>\n' %subdir_name
893+
page_html+=' <td class="sticky-col" style="font-weight:bold;">%s</td>\n' %subdir_name
867894

868895
row_summary=Directory("row_summary","")
869896
sample_counter=0
@@ -878,9 +905,10 @@ def make_summary_table(indir,aggregation_rules,aggregation_rules_twiki, hashing_
878905

879906
# one first row for the summary!
880907
row_summary.calcStats()
881-
img_link=row_summary.get_summary_chart_ajax(55,55)
882-
page_html+='<td style="background-color:white;"><div class="span-1">'
883-
page_html+='<img src="%s" title="%s"></div></td>' %(img_link,get_pie_tooltip(row_summary))
908+
title = get_pie_tooltip(col)
909+
chart = row_summary.get_piechart_js(50)
910+
page_html+='<td><div>'#<div class="span-1">'
911+
page_html+= chart + '</div></td>'
884912

885913
for sample in sorted_samples:
886914
sample_counter+=1
@@ -903,30 +931,27 @@ def make_summary_table(indir,aggregation_rules,aggregation_rules_twiki, hashing_
903931
summary_page=join(sample,"%s.html"%(hash_name(directory.name+subdir_name,hashing_flag)))
904932
dir_is_there=subdir_name in subdirs_dict
905933

906-
img_link="https://chart.googleapis.com/chart?cht=p3&chco=C0C0C0&chs=50x50&chd=t:1"
907-
img_tooltip="N/A"
908-
if dir_is_there:
909-
#row_summary.subdirs.append(subdirs_dict[subdir_name])
910-
img_link=subdirs_dict[subdir_name].get_summary_chart_ajax(50,50)
911-
img_tooltip=get_pie_tooltip(subdirs_dict[subdir_name])
934+
img_link="https://upload.wikimedia.org/wikipedia/commons/a/a8/Circle_Davys-Grey_Solid.svg"
935+
page_html+='<td><div class="span-1">'
912936

913-
page_html+='<td style="background-color:white;"><div class="span-1">'
914-
if dir_is_there:
915-
page_html+='<a href="%s">'%(summary_page)
916-
page_html+='<img src="%s" title="%s" height=50 width=50>' %(img_link,img_tooltip)
917937
if dir_is_there:
918-
page_html+='</a>'
938+
#row_summary.subdirs.append(subdirs_dict[subdir_name])
939+
chart=subdirs_dict[subdir_name].get_piechart_js(50,summary_page)
940+
page_html+='%s'%chart
941+
else:
942+
page_html+='<img src="%s" title="%s" height=50 width=50>' %(img_link,"Unavailable")
919943
page_html+='</div></td>'
920944

921945
page_html+=" </tr>\n"
922946

923947

924948

925-
page_html+='</table> <a href="#top">Top...</a><hr>'
949+
page_html+='</tbody> </table> </div> <a href="#top">Top...</a><hr>'
926950

927951
page_html+=get_rank_section(global_dir)
928952

929-
page_html+=make_twiki_table(dir_dict,aggregation_rules_twiki)
953+
#page_html+=make_twiki_table(dir_dict,aggregation_rules_twiki)
954+
# ^ commenting out for the moment, not really useful nor used
930955

931956
page_html+=get_page_footer()
932957
return page_html

Utilities/RelMon/python/dirstructure.py

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from copy import deepcopy
1616
from os import chdir,getcwd,listdir,makedirs,rmdir
1717
from os.path import exists,join
18+
import random
1819

1920
import sys
2021
argv=sys.argv
@@ -167,19 +168,45 @@ def get_subdirs_names(self):
167168
subdirnames.append(subdir.name)
168169
return subdirnames
169170

170-
def get_summary_chart_ajax(self,w=400,h=300):
171-
"""Emit the ajax to build a pie chart using google apis...
171+
def get_piechart_js(self,w=400,link=None):
172+
173+
"""
174+
Build the HTML snippet to render a piechart with chart.js
172175
"""
173-
url = "https://chart.googleapis.com/chart?"
174-
url+= "cht=p3" # Select the 3d chart
175-
#url+= "&chl=Success|Null|Fail" # give labels
176-
url+= "&chco=00FF00|FFFF00|FF0000|7A7A7A" # give colours to labels
177-
url+= "&chs=%sx%s" %(w,h)
178-
#url+= "&chtt=%s" %self.name
179-
url+= "&chd=t:%.2f,%.2f,%.2f,%.2f"%(self.get_success_rate(),self.get_null_rate(),self.get_fail_rate(),self.get_skiped_rate())
176+
if self.get_success_rate()>=99.9: # if the success rate is very high let's make the page lighter
177+
img_link = "https://raw.githubusercontent.com/cms-PdmV/RelMonService2/5ee98db210c0898fd34b4deac3653fa2bdff269b/report_website/lime_circle.png"
178+
html ='<img src="%s" height=%d width=%d>' %(img_link,w,w)
179+
if link is not None:
180+
html = '<a href="%s"> %s </a>' %(link,html)
181+
return html
182+
183+
name = random.getrandbits(64) # just a random has for the canvas
184+
html = ""
185+
html += '<canvas id="%s" height=%d width=%d></canvas>'%(name,w,w)
186+
# piechart
187+
html += '<script> new Chart("%s",'%(name)
188+
html += '{ type: "pie",'
189+
190+
# data
191+
html += 'data: {'
192+
html += 'labels: ["Success", "Null" , "Failure", "Skipped"],'
193+
html += 'datasets: [{ backgroundColor: ["lime","yellow","red","grey"],'
194+
html += 'data: [%.2f,%.2f,%.2f,%.2f]}] },'%(self.get_success_rate(),self.get_null_rate(),self.get_fail_rate(),self.get_skiped_rate())
180195

181-
return url
196+
#display options
197+
html += 'options: { '
198+
199+
if link is not None:
200+
html += 'onClick : function(event) { window.open("%s", "_blank");},'%(link)
201+
182202

203+
html +='legend: { display: false }, responsive : false, hover: {mode: null}, tooltips: {enabled: false}'
204+
#tooltips: {enabled: false}, hover: {mode: null},'
205+
206+
html += '}}); </script>'
207+
208+
return html
209+
183210
def print_report(self,indent="",verbose=False):
184211
if len(indent)==0:
185212
self.calcStats(make_pie=False)

0 commit comments

Comments
 (0)