Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/python_nvd3-0.16.0-py3.11.egg/nvd3/NVD3Chart.py: 6%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

208 statements  

1#!/usr/bin/python 

2# -*- coding: utf-8 -*- 

3 

4""" 

5Python-nvd3 is a Python wrapper for NVD3 graph library. 

6NVD3 is an attempt to build re-usable charts and chart components 

7for d3.js without taking away the power that d3.js gives you. 

8 

9Project location : https://github.com/areski/python-nvd3 

10""" 

11 

12from __future__ import unicode_literals 

13from optparse import OptionParser 

14from jinja2 import Environment, PackageLoader 

15from slugify import slugify 

16try: 

17 import simplejson as json 

18except ImportError: 

19 import json 

20 

21CONTENT_FILENAME = "./content.html" 

22PAGE_FILENAME = "./page.html" 

23 

24 

25pl = PackageLoader('nvd3', 'templates') 

26jinja2_env = Environment(lstrip_blocks=True, trim_blocks=True, loader=pl) 

27 

28template_content = jinja2_env.get_template(CONTENT_FILENAME) 

29template_page = jinja2_env.get_template(PAGE_FILENAME) 

30 

31 

32def stab(tab=1): 

33 """ 

34 create space tabulation 

35 """ 

36 return ' ' * 4 * tab 

37 

38 

39class NVD3Chart(object): 

40 """ 

41 NVD3Chart Base class. 

42 """ 

43 #: chart count 

44 count = 0 

45 #: directory holding the assets (bower_components) 

46 assets_directory = './bower_components/' 

47 

48 # this attribute is overridden by children of this 

49 # class 

50 CHART_FILENAME = None 

51 template_environment = Environment(lstrip_blocks=True, trim_blocks=True, 

52 loader=pl) 

53 

54 def __init__(self, **kwargs): 

55 """ 

56 This is the base class for all the charts. The following keywords are 

57 accepted: 

58 

59 :keyword: **display_container** - default: ``True`` 

60 :keyword: **jquery_on_ready** - default: ``False`` 

61 :keyword: **charttooltip_dateformat** - default: ``'%d %b %Y'`` 

62 :keyword: **name** - default: the class name 

63 ``model`` - set the model (e.g. ``pieChart``, ` 

64 ``LineWithFocusChart``, ``MultiBarChart``). 

65 :keyword: **color_category** - default - ``None`` 

66 :keyword: **color_list** - default - ``None`` 

67 used by pieChart (e.g. ``['red', 'blue', 'orange']``) 

68 :keyword: **margin_bottom** - default - ``20`` 

69 :keyword: **margin_left** - default - ``60`` 

70 :keyword: **margin_right** - default - ``60`` 

71 :keyword: **margin_top** - default - ``30`` 

72 :keyword: **height** - default - ``''`` 

73 :keyword: **width** - default - ``''`` 

74 :keyword: **show_values** - default - ``False`` 

75 :keyword: **stacked** - default - ``False`` 

76 :keyword: **focus_enable** - default - ``False`` 

77 :keyword: **resize** - define - ``False`` 

78 :keyword: **no_data_message** - default - ``None`` or nvd3 default 

79 :keyword: **xAxis_rotateLabel** - default - ``0`` 

80 :keyword: **xAxis_staggerLabel** - default - ``False`` 

81 :keyword: **xAxis_showMaxMin** - default - ``True`` 

82 :keyword: **right_align_y_axis** - default - ``False`` 

83 :keyword: **show_controls** - default - ``True`` 

84 :keyword: **show_legend** - default - ``True`` 

85 :keyword: **show_labels** - default - ``True`` 

86 :keyword: **tag_script_js** - default - ``True`` 

87 :keyword: **use_interactive_guideline** - default - ``False`` 

88 :keyword: **chart_attr** - default - ``None`` 

89 :keyword: **extras** - default - ``None`` 

90 

91 Extra chart modifiers. Use this to modify different attributes of 

92 the chart. 

93 :keyword: **x_axis_date** - default - False 

94 Signal that x axis is a date axis 

95 :keyword: **date_format** - default - ``%x`` 

96 see https://github.com/mbostock/d3/wiki/Time-Formatting 

97 :keyword: **y_axis_scale_min** - default - ``''``. 

98 :keyword: **y_axis_scale_max** - default - ``''``. 

99 :keyword: **x_axis_format** - default - ``''``. 

100 :keyword: **y_axis_format** - default - ``''``. 

101 :keyword: **style** - default - ``''`` 

102 Style modifiers for the DIV container. 

103 :keyword: **color_category** - default - ``category10`` 

104 

105 Acceptable values are nvd3 categories such as 

106 ``category10``, ``category20``, ``category20c``. 

107 """ 

108 # set the model 

109 self.model = self.__class__.__name__ #: The chart model, 

110 

111 #: an Instance of Jinja2 template 

112 self.template_page_nvd3 = template_page 

113 self.template_content_nvd3 = template_content 

114 self.series = [] 

115 self.axislist = {} 

116 # accepted keywords 

117 self.display_container = kwargs.get('display_container', True) 

118 self.charttooltip_dateformat = kwargs.get('charttooltip_dateformat', 

119 '%d %b %Y') 

120 self._slugify_name(kwargs.get('name', self.model)) 

121 self.jquery_on_ready = kwargs.get('jquery_on_ready', False) 

122 self.color_category = kwargs.get('color_category', None) 

123 self.color_list = kwargs.get('color_list', None) 

124 self.margin_bottom = kwargs.get('margin_bottom', 20) 

125 self.margin_left = kwargs.get('margin_left', 60) 

126 self.margin_right = kwargs.get('margin_right', 60) 

127 self.margin_top = kwargs.get('margin_top', 30) 

128 self.height = kwargs.get('height', '') 

129 self.width = kwargs.get('width', '') 

130 self.show_values = kwargs.get('show_values', False) 

131 self.stacked = kwargs.get('stacked', False) 

132 self.focus_enable = kwargs.get('focus_enable', False) 

133 self.resize = kwargs.get('resize', False) 

134 self.no_data_message = kwargs.get('no_data_message', None) 

135 self.xAxis_rotateLabel = kwargs.get('xAxis_rotateLabel', 0) 

136 self.xAxis_staggerLabel = kwargs.get('xAxis_staggerLabel', False) 

137 self.xAxis_showMaxMin = kwargs.get('xAxis_showMaxMin', True) 

138 self.right_align_y_axis = kwargs.get('right_align_y_axis', False) 

139 self.show_controls = kwargs.get('show_controls', True) 

140 self.show_legend = kwargs.get('show_legend', True) 

141 self.show_labels = kwargs.get('show_labels', True) 

142 self.tooltip_separator = kwargs.get('tooltip_separator') 

143 self.tag_script_js = kwargs.get('tag_script_js', True) 

144 self.use_interactive_guideline = kwargs.get("use_interactive_guideline", 

145 False) 

146 self.chart_attr = kwargs.get("chart_attr", {}) 

147 self.extras = kwargs.get('extras', None) 

148 self.style = kwargs.get('style', '') 

149 self.date_format = kwargs.get('date_format', '%x') 

150 self.x_axis_date = kwargs.get('x_axis_date', False) 

151 self.y_axis_scale_min = kwargs.get('y_axis_scale_min', '') 

152 self.y_axis_scale_max = kwargs.get('y_axis_scale_max', '') 

153 #: x-axis contain date format or not 

154 # possible duplicate of x_axis_date 

155 self.date_flag = kwargs.get('date_flag', False) 

156 self.x_axis_format = kwargs.get('x_axis_format', '') 

157 # Load remote JS assets or use the local bower assets? 

158 self.remote_js_assets = kwargs.get('remote_js_assets', True) 

159 self.callback = kwargs.get('callback', None) 

160 

161 # None keywords attribute that should be modified by methods 

162 # We should change all these to _attr 

163 

164 self.htmlcontent = '' #: written by buildhtml 

165 self.htmlheader = '' 

166 #: Place holder for the graph (the HTML div) 

167 #: Written by ``buildcontainer`` 

168 self.container = u'' 

169 #: Header for javascript code 

170 self.containerheader = u'' 

171 # CDN http://cdnjs.com/libraries/nvd3/ needs to make sure it's up to 

172 # date 

173 self.header_css = [ 

174 '<link href="%s" rel="stylesheet" />' % h for h in 

175 ( 

176 'https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.6/nv.d3.min.css' if self.remote_js_assets else self.assets_directory + 'nvd3/src/nv.d3.css', 

177 ) 

178 ] 

179 

180 self.header_js = [ 

181 '<script src="%s"></script>' % h for h in 

182 ( 

183 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js' if self.remote_js_assets else self.assets_directory + 'd3/d3.min.js', 

184 'https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.6/nv.d3.min.js' if self.remote_js_assets else self.assets_directory + 'nvd3/nv.d3.min.js' 

185 ) 

186 ] 

187 

188 #: Javascript code as string 

189 self.jschart = None 

190 self.custom_tooltip_flag = False 

191 self.charttooltip = '' 

192 self.serie_no = 1 

193 

194 def _slugify_name(self, name): 

195 """Slufigy name with underscore""" 

196 self.name = slugify(name, separator='_') 

197 

198 def add_serie(self, y, x, name=None, extra=None, **kwargs): 

199 """ 

200 add serie - Series are list of data that will be plotted 

201 y {1, 2, 3, 4, 5} / x {1, 2, 3, 4, 5} 

202 

203 **Attributes**: 

204 

205 * ``name`` - set Serie name 

206 * ``x`` - x-axis data 

207 * ``y`` - y-axis data 

208 

209 kwargs: 

210 

211 * ``shape`` - for scatterChart, you can set different shapes 

212 (circle, triangle etc...) 

213 * ``size`` - for scatterChart, you can set size of different shapes 

214 * ``type`` - for multiChart, type should be bar 

215 * ``bar`` - to display bars in Chart 

216 * ``color_list`` - define list of colors which will be 

217 used by pieChart 

218 * ``color`` - set axis color 

219 * ``disabled`` - 

220 

221 extra: 

222 

223 * ``tooltip`` - set tooltip flag 

224 * ``date_format`` - set date_format for tooltip if x-axis is in 

225 date format 

226 

227 """ 

228 if not name: 

229 name = "Serie %d" % (self.serie_no) 

230 

231 # For scatterChart shape & size fields are added in serie 

232 if 'shape' in kwargs or 'size' in kwargs: 

233 csize = kwargs.get('size', 1) 

234 cshape = kwargs.get('shape', 'circle') 

235 

236 serie = [{ 

237 'x': x[i], 

238 'y': j, 

239 'shape': cshape, 

240 'size': csize[i] if isinstance(csize, list) else csize 

241 } for i, j in enumerate(y)] 

242 else: 

243 if self.model == 'pieChart': 

244 serie = [{'label': x[i], 'value': y} for i, y in enumerate(y)] 

245 else: 

246 serie = [{'x': x[i], 'y': y} for i, y in enumerate(y)] 

247 

248 data_keyvalue = {'values': serie, 'key': name} 

249 

250 # multiChart 

251 # Histogram type='bar' for the series 

252 if 'type' in kwargs and kwargs['type']: 

253 data_keyvalue['type'] = kwargs['type'] 

254 

255 # Define on which Y axis the serie is related 

256 # a chart can have 2 Y axis, left and right, by default only one Y Axis is used 

257 if 'yaxis' in kwargs and kwargs['yaxis']: 

258 data_keyvalue['yAxis'] = kwargs['yaxis'] 

259 else: 

260 if self.model != 'pieChart': 

261 data_keyvalue['yAxis'] = '1' 

262 

263 if 'bar' in kwargs and kwargs['bar']: 

264 data_keyvalue['bar'] = 'true' 

265 

266 if 'disabled' in kwargs and kwargs['disabled']: 

267 data_keyvalue['disabled'] = 'true' 

268 

269 if 'color' in kwargs and kwargs['color']: 

270 data_keyvalue['color'] = kwargs['color'] 

271 

272 if extra: 

273 if self.model == 'pieChart': 

274 if 'color_list' in extra and extra['color_list']: 

275 self.color_list = extra['color_list'] 

276 

277 if extra.get('date_format'): 

278 self.charttooltip_dateformat = extra['date_format'] 

279 

280 if extra.get('tooltip'): 

281 self.custom_tooltip_flag = True 

282 

283 if self.model != 'pieChart': 

284 _start = extra['tooltip']['y_start'] 

285 _end = extra['tooltip']['y_end'] 

286 _start = ("'" + str(_start) + "' + ") if _start else '' 

287 _end = (" + '" + str(_end) + "'") if _end else '' 

288 

289 if self.model == 'pieChart': 

290 _start = extra['tooltip']['y_start'] 

291 _end = extra['tooltip']['y_end'] 

292 _start = ("'" + str(_start) + "' + ") if _start else '' 

293 _end = (" + '" + str(_end) + "'") if _end else '' 

294 

295 # Increment series counter & append 

296 self.serie_no += 1 

297 self.series.append(data_keyvalue) 

298 

299 def add_chart_extras(self, extras): 

300 """ 

301 Use this method to add extra d3 properties to your chart. 

302 For example, you want to change the text color of the graph:: 

303 

304 chart = pieChart(name='pieChart', color_category='category20c', height=400, width=400) 

305 

306 xdata = ["Orange", "Banana", "Pear", "Kiwi", "Apple", "Strawberry", "Pineapple"] 

307 ydata = [3, 4, 0, 1, 5, 7, 3] 

308 

309 extra_serie = {"tooltip": {"y_start": "", "y_end": " cal"}} 

310 chart.add_serie(y=ydata, x=xdata, extra=extra_serie) 

311 

312 The above code will create graph with a black text, the following will change it:: 

313 

314 text_white="d3.selectAll('#pieChart text').style('fill', 'white');" 

315 chart.add_chart_extras(text_white) 

316 

317 The above extras will be appended to the java script generated. 

318 

319 Alternatively, you can use the following initialization:: 

320 

321 chart = pieChart(name='pieChart', 

322 color_category='category20c', 

323 height=400, width=400, 

324 extras=text_white) 

325 """ 

326 self.extras = extras 

327 

328 def set_graph_height(self, height): 

329 """Set Graph height""" 

330 self.height = str(height) 

331 

332 def set_graph_width(self, width): 

333 """Set Graph width""" 

334 self.width = str(width) 

335 

336 def set_containerheader(self, containerheader): 

337 """Set containerheader""" 

338 self.containerheader = containerheader 

339 

340 def set_date_flag(self, date_flag=False): 

341 """Set date flag""" 

342 self.date_flag = date_flag 

343 

344 def set_custom_tooltip_flag(self, custom_tooltip_flag): 

345 """Set custom_tooltip_flag & date_flag""" 

346 self.custom_tooltip_flag = custom_tooltip_flag 

347 

348 def __str__(self): 

349 """return htmlcontent""" 

350 self.buildhtml() 

351 return self.htmlcontent 

352 

353 def buildcontent(self): 

354 """Build HTML content only, no header or body tags. To be useful this 

355 will usually require the attribute `jquery_on_ready` to be set which 

356 will wrap the js in $(function(){<regular_js>};) 

357 """ 

358 self.buildcontainer() 

359 # if the subclass has a method buildjs this method will be 

360 # called instead of the method defined here 

361 # when this subclass method is entered it does call 

362 # the method buildjschart defined here 

363 self.buildjschart() 

364 self.htmlcontent = self.template_content_nvd3.render(chart=self) 

365 

366 def buildhtml(self): 

367 """Build the HTML page 

368 Create the htmlheader with css / js 

369 Create html page 

370 Add Js code for nvd3 

371 """ 

372 self.buildcontent() 

373 self.content = self.htmlcontent 

374 self.htmlcontent = self.template_page_nvd3.render(chart=self) 

375 

376 # this is used by django-nvd3 

377 def buildhtmlheader(self): 

378 """generate HTML header content""" 

379 self.htmlheader = '' 

380 # If the JavaScript assets have already been injected, don't bother re-sourcing them. 

381 global _js_initialized 

382 if '_js_initialized' not in globals() or not _js_initialized: 

383 for css in self.header_css: 

384 self.htmlheader += css 

385 for js in self.header_js: 

386 self.htmlheader += js 

387 

388 def buildcontainer(self): 

389 """generate HTML div""" 

390 if self.container: 

391 return 

392 

393 # Create SVG div with style 

394 if self.width: 

395 if self.width[-1] != '%': 

396 self.style += 'width:%spx;' % self.width 

397 else: 

398 self.style += 'width:%s;' % self.width 

399 if self.height: 

400 if self.height[-1] != '%': 

401 self.style += 'height:%spx;' % self.height 

402 else: 

403 self.style += 'height:%s;' % self.height 

404 if self.style: 

405 self.style = 'style="%s"' % self.style 

406 

407 self.container = self.containerheader + \ 

408 '<div id="%s"><svg %s></svg></div>\n' % (self.name, self.style) 

409 

410 def buildjschart(self): 

411 """generate javascript code for the chart""" 

412 self.jschart = '' 

413 

414 # Include data 

415 self.series_js = json.dumps(self.series) 

416 

417 def create_x_axis(self, name, label=None, format=None, date=False, custom_format=False): 

418 """Create X-axis""" 

419 axis = {} 

420 if custom_format and format: 

421 axis['tickFormat'] = format 

422 elif format: 

423 if format == 'AM_PM': 

424 axis['tickFormat'] = "function(d) { return get_am_pm(parseInt(d)); }" 

425 else: 

426 axis['tickFormat'] = "d3.format(',%s')" % format 

427 

428 if label: 

429 axis['axisLabel'] = "'" + label + "'" 

430 

431 # date format : see https://github.com/mbostock/d3/wiki/Time-Formatting 

432 if date: 

433 self.dateformat = format 

434 axis['tickFormat'] = ("function(d) { return d3.time.format('%s')" 

435 "(new Date(parseInt(d))) }\n" 

436 "" % self.dateformat) 

437 # flag is the x Axis is a date 

438 if name[0] == 'x': 

439 self.x_axis_date = True 

440 

441 # Add new axis to list of axis 

442 self.axislist[name] = axis 

443 

444 # Create x2Axis if focus_enable 

445 if name == "xAxis" and self.focus_enable: 

446 self.axislist['x2Axis'] = axis 

447 

448 def create_y_axis(self, name, label=None, format=None, custom_format=False): 

449 """ 

450 Create Y-axis 

451 """ 

452 axis = {} 

453 

454 if custom_format and format: 

455 axis['tickFormat'] = format 

456 elif format: 

457 axis['tickFormat'] = "d3.format(',%s')" % format 

458 

459 if label: 

460 axis['axisLabel'] = "'" + label + "'" 

461 

462 # Add new axis to list of axis 

463 self.axislist[name] = axis 

464 

465 

466class TemplateMixin(object): 

467 """ 

468 A mixin that override buildcontent. Instead of building the complex 

469 content template we exploit Jinja2 inheritance. Thus each chart class 

470 renders it's own chart template which inherits from content.html 

471 """ 

472 def buildcontent(self): 

473 """Build HTML content only, no header or body tags. To be useful this 

474 will usually require the attribute `jquery_on_ready` to be set which 

475 will wrap the js in $(function(){<regular_js>};) 

476 """ 

477 self.buildcontainer() 

478 # if the subclass has a method buildjs this method will be 

479 # called instead of the method defined here 

480 # when this subclass method is entered it does call 

481 # the method buildjschart defined here 

482 self.buildjschart() 

483 self.htmlcontent = self.template_chart_nvd3.render(chart=self) 

484 

485 

486def _main(): 

487 """ 

488 Parse options and process commands 

489 """ 

490 # Parse arguments 

491 usage = "usage: nvd3.py [options]" 

492 parser = OptionParser(usage=usage, 

493 version=("python-nvd3 - Charts generator with " 

494 "nvd3.js and d3.js")) 

495 parser.add_option("-q", "--quiet", 

496 action="store_false", dest="verbose", default=True, 

497 help="don't print messages to stdout") 

498 

499 (options, args) = parser.parse_args() 

500 

501 

502if __name__ == '__main__': 

503 _main()