iwla

iwla Git Source Tree

Root/display.py

1# -*- coding: utf-8 -*-
2#
3# Copyright Grégory Soutadé 2015
4
5# This file is part of iwla
6
7# iwla is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# iwla is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with iwla. If not, see <http://www.gnu.org/licenses/>.
19#
20
21import os
22import codecs
23import time
24import logging
25
26#
27# Create output HTML files
28#
29
30# Just for detection
31def _(name): pass
32_('January'), _('February'), _('March'), _('April'), _('May'), _('June'), _('July')
33_('August'), _('September'), _('October'), _('November'), _('December')
34del _
35
36class DisplayHTMLRaw(object):
37
38 def __init__(self, iwla, html=u''):
39 self.iwla = iwla
40 self.html = html
41
42 def setRawHTML(self, html):
43 self.html = html
44
45 def _buildHTML(self):
46 pass
47
48 def _build(self, f, html):
49 if html: f.write(html)
50
51 def build(self, f, filters=None):
52 if filters: self.filter(filters)
53 self._buildHTML()
54 self._build(f, self.html)
55
56 def _filter(self, function, **kwargs):
57 pass
58
59 def filter(self, filters):
60 for (args, function) in filters:
61 self._filter(function, **args)
62
63 def getTitle(self):
64 return ''
65
66class DisplayHTMLBlock(DisplayHTMLRaw):
67
68 def __init__(self, iwla, title=''):
69 super(DisplayHTMLBlock, self).__init__(iwla, html='')
70 self.title = title
71 self.cssclass = u'iwla_block'
72 self.title_cssclass = u'iwla_block_title'
73 self.value_cssclass = u'iwla_block_value'
74
75 def getTitle(self):
76 return self.title
77
78 def setTitle(self, value):
79 self.title = value
80
81 def setCSSClass(self, cssclass):
82 self.cssclass = cssclass
83
84 def setTitleCSSClass(self, cssclass):
85 self.title_cssclass = cssclass
86
87 def setValueCSSClass(self, cssclass):
88 self.value_cssclass = cssclass
89
90 def _buildHTML(self):
91 html = u'<div class="%s">' % (self.cssclass)
92 if self.title:
93 html += u'<div class="%s">%s</div>' % (self.title_cssclass, self.title)
94 html += u'<div class="%s">%s</div>' % (self.value_cssclass, self.html)
95 html += u'</div>'
96
97 self.html = html
98
99class DisplayHTMLBlockTable(DisplayHTMLBlock):
100
101 def __init__(self, iwla, title, cols):
102 super(DisplayHTMLBlockTable, self).__init__(iwla=iwla, title=title)
103 self.cols = listToStr(cols)
104 self.rows = []
105 self.cols_cssclasses = [u''] * len(cols)
106 self.rows_cssclasses = []
107 self.table_css = u'iwla_table'
108
109 def appendRow(self, row):
110 self.rows.append(listToStr(row))
111 self.rows_cssclasses.append([u''] * len(row))
112
113 def insertCol(self, col_number, col_title='', col_css_class=''):
114 self.cols.insert(col_number, col_title)
115 for r in self.rows:
116 r.insert(col_number, u'')
117 for r in self.rows_cssclasses:
118 v = r[0]
119 # If all cells have the same CSS class, set it
120 for cur_value in r:
121 if v != cur_value:
122 v = None
123 break
124 v = v or u''
125 r.insert(col_number, v)
126 self.cols_cssclasses.insert(col_number, col_css_class)
127
128 def getNbRows(self):
129 return len(self.rows)
130
131 def getNbCols(self):
132 return len(self.cols)
133
134 def getCellValue(self, row, col):
135 if row < 0 or col < 0 or\
136 row >= len(self.rows) or col >= len(self.cols):
137 raise ValueError('Invalid indices %d,%d' % (row, col))
138
139 return self.rows[row][col]
140
141 def setCellValue(self, row, col, value):
142 if row < 0 or col < 0 or\
143 row >= len(self.rows) or col >= len(self.cols):
144 raise ValueError('Invalid indices %d,%d' % (row, col))
145
146 self.rows[row][col] = value
147
148 def setCellCSSClass(self, row, col, value):
149 if row < 0 or col < 0 or\
150 row >= len(self.rows) or col >= len(self.cols):
151 raise ValueError('Invalid indices %d,%d' % (row, col))
152
153 self.rows_cssclasses[row][col] = value
154
155 def getCellCSSClass(self, row, col):
156 if row < 0 or col < 0 or\
157 row >= len(self.rows) or col >= len(self.cols):
158 raise ValueError('Invalid indices %d,%d' % (row, col))
159
160 return self.rows_cssclasses[row][col]
161
162 def getColCSSClass(self, col):
163 if col < 0 or col >= len(self.cols):
164 raise ValueError('Invalid indice %d' % (col))
165
166 return self.cols_cssclasses[col]
167
168 def setRowCSSClass(self, row, value):
169 if row < 0 or row >= len(self.rows):
170 raise ValueError('Invalid indice %d' % (row))
171
172 self.rows_cssclasses[row] = [value] * len(self.rows_cssclasses[row])
173
174 def setColCSSClass(self, col, value):
175 if col < 0 or col >= len(self.cols):
176 raise ValueError('Invalid indice %d' % (col))
177
178 self.cols_cssclasses[col] = value
179
180 def setColsCSSClass(self, values):
181 if len(values) != len(self.cols):
182 raise ValueError('Invalid values size')
183
184 self.cols_cssclasses = listToStr(values)
185
186 def computeRatio(self, column, column_insertion=None):
187 if column_insertion is None:
188 column_insertion = column+1
189
190 total = 0
191 for r in self.rows:
192 if r[column]:
193 total += int(r[column])
194
195 self.insertCol(column_insertion, self.iwla._('Ratio'), u'iwla_hit')
196 for (index, r) in enumerate(self.rows):
197 val = r[column] and int(r[column]) or 0
198 self.setCellValue(index, column_insertion, '%.1f%%' % (float(val*100)/float(total)))
199
200 def _filter(self, function, column, args):
201 target_col = None
202 for col in range(0, len(self.cols)):
203 if self.cols[col] == column:
204 target_col = col
205 break
206 if target_col is None: return
207 for row in self.rows:
208 res = function(row[target_col], **args)
209 if res:
210 row[target_col] = res
211
212 def _buildHTML(self):
213 style = u''
214 if self.table_css: style = u' class="%s"' % (self.table_css)
215 html = u'<table%s>' % (style)
216 if self.cols:
217 html += u'<tr>'
218 for i in range (0, len(self.cols)):
219 title = self.cols[i]
220 style = self.getColCSSClass(i)
221 if style: style = u' class="%s"' % (style)
222 html += u'<th%s>%s</th>' % (style, title)
223 html += u'</tr>'
224 for i in range(0, len(self.rows)):
225 row = self.rows[i]
226 html += u'<tr>'
227 for j in range(0, len(row)):
228 v = row[j]
229 style = self.getCellCSSClass(i, j)
230 if style: style = u' class="%s"' % (style)
231 html += u'<td%s>%s</td>' % (style, v)
232 html += u'</tr>'
233 html += u'</table>'
234
235 self.html += html
236
237 super(DisplayHTMLBlockTable, self)._buildHTML()
238
239class DisplayHTMLBlockTableWithGraph(DisplayHTMLBlockTable):
240
241 def __init__(self, iwla, title, cols, short_titles=None, nb_valid_rows=0, graph_cols=None):
242 super(DisplayHTMLBlockTableWithGraph, self).__init__(iwla=iwla, title=title, cols=cols)
243 self.short_titles = short_titles or []
244 self.short_titles = listToStr(self.short_titles)
245 self.nb_valid_rows = nb_valid_rows
246 self.icon_path = self.iwla.getConfValue('icon_path', '/')
247 self.raw_rows = []
248 self.maxes = [0] * len(cols)
249 self.table_graph_css = u'iwla_graph_table'
250 self.td_img_css = u'iwla_td_img'
251 self.graph_cols = graph_cols or []
252
253 def appendRow(self, row):
254 self.raw_rows.append(row)
255 super(DisplayHTMLBlockTableWithGraph, self).appendRow(row)
256
257 def appendShortTitle(self, short_title):
258 self.short_titles.append(short_title)
259
260 def setShortTitle(self, short_titles):
261 self.short_titles = listToStr(short_titles)
262
263 def setNbValidRows(self, nb_valid_rows):
264 self.nb_valid_rows = nb_valid_rows
265
266 def _computeMax(self):
267 for i in range(0, self.nb_valid_rows):
268 row = self.raw_rows[i]
269 for j in range(1, len(row)):
270 try:
271 if row[j] > self.maxes[j]:
272 self.maxes[j] = row[j]
273 except:
274 continue
275
276 def _getIconFromStyle(self, style):
277 if style.startswith(u'iwla_page'): icon = u'vp.png'
278 elif style.startswith(u'iwla_hit'): icon = u'vh.png'
279 elif style.startswith(u'iwla_bandwidth'): icon = u'vk.png'
280 elif style.startswith(u'iwla_visitor'): icon = u'vu.png'
281 elif style.startswith(u'iwla_visit'): icon = u'vv.png'
282 else: return ''
283
284 return u'/%s/other/%s' % (self.icon_path, icon)
285
286 def _buildHTML(self):
287 self._computeMax()
288
289 style = u''
290 if self.table_graph_css: style = u' class="%s"' % (self.table_graph_css)
291 html = u'<table%s>' % (style)
292 html += u'<tr>'
293 for i in range(0, self.nb_valid_rows):
294 row = self.rows[i]
295 css = u''
296 if self.td_img_css: css=u' class="%s"' % (self.td_img_css)
297 html += u'<td%s>' % (css)
298 for j in self.graph_cols:
299 style = self.getColCSSClass(j)
300 icon = self._getIconFromStyle(style)
301 if not icon: continue
302 if style: style = u' class="%s"' % (style)
303 alt = u'%s: %s' % (row[j], self.cols[j])
304 if self.maxes[j]:
305 height = int((self.raw_rows[i][j] * 100) / self.maxes[j]) or 1
306 else:
307 height = 1
308 html += u'<img%s src="%s" height="%d" width="6" alt="%s" title="%s" />' % (style, icon, height, alt, alt)
309 html += u'</td>'
310 html += u'</tr>'
311 html += u'<tr>'
312 for i in range(0, len(self.short_titles)):
313 style = self.getCellCSSClass(i, 0)
314 if style: style = u' class="%s"' % (style)
315 html += u'<td%s>%s</td>' % (style, self.short_titles[i])
316 html += u'</tr>'
317 html += u'</table>'
318
319 self.html += html
320
321 super(DisplayHTMLBlockTableWithGraph, self)._buildHTML()
322
323class DisplayHTMLPage(object):
324
325 def __init__(self, iwla, title, filename, css_path):
326 self.iwla = iwla
327 self.title = title
328 self.filename = filename
329 self.blocks = []
330 self.css_path = listToStr(css_path)
331 self.logger = logging.getLogger(self.__class__.__name__)
332
333 def getFilename(self):
334 return self.filename;
335
336 def getBlock(self, title):
337 for b in self.blocks:
338 if title == b.getTitle():
339 return b
340 return None
341
342 def getAllBlocks(self):
343 return self.blocks
344
345 def appendBlock(self, block):
346 self.blocks.append(block)
347
348 def build(self, root, displayVersion=True, filters=None):
349 filename = os.path.join(root, self.filename)
350
351 base = os.path.dirname(filename)
352 if not os.path.exists(base):
353 os.makedirs(base)
354
355 self.logger.debug('Write %s' % (filename))
356
357 if self.iwla.dry_run: return
358
359 f = codecs.open(filename, 'w', 'utf-8')
360 f.write(u'<!DOCTYPE html>')
361 f.write(u'<html>')
362 f.write(u'<head>')
363 f.write(u'<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />')
364 for css in self.css_path:
365 f.write(u'<link rel="stylesheet" href="/%s"/>' % (css))
366 if self.title:
367 f.write(u'<title>%s</title>' % (self.title))
368 f.write(u'</head><body>')
369 for block in self.blocks:
370 block.build(f, filters=filters)
371 if displayVersion:
372 f.write(u'<div style="text-align:center;width:100%%">Generated by <a href="%s">IWLA %s</a></div>' %
373 ("http://indefero.soutade.fr/p/iwla", self.iwla.getVersion()))
374 f.write(u'</body></html>')
375 f.close()
376
377class DisplayHTMLBuild(object):
378
379 def __init__(self, iwla):
380 self.iwla = iwla
381 self.filters = []
382 self.clear()
383
384 def clear(self):
385 self.pages = []
386
387 def createPage(self, *args):
388 return DisplayHTMLPage(self.iwla, *args)
389
390 def createBlock(self, _class, *args):
391 return _class(self.iwla, *args)
392
393 def getPage(self, filename):
394 for page in self.pages:
395 if page.getFilename() == filename:
396 return page
397 return None
398
399 def getAllPages(self):
400 return self.pages
401
402 def addPage(self, page):
403 self.pages.append(page)
404
405 def build(self, root):
406 if not self.iwla.dry_run:
407 display_root = self.iwla.getConfValue('DISPLAY_ROOT', '')
408 if not os.path.exists(display_root):
409 os.makedirs(display_root)
410 for res_path in self.iwla.getResourcesPath():
411 target = os.path.abspath(res_path)
412 link_name = os.path.join(display_root, res_path)
413 if not os.path.exists(link_name):
414 os.symlink(target, link_name)
415
416 for page in self.pages:
417 page.build(root, filters=self.filters)
418
419 def addColumnFilter(self, column, function, args):
420 self.filters.append(({'column':column, 'args':args}, function))
421
422
423#
424# Global functions
425#
426
427def bytesToStr(_bytes):
428 suffixes = [u'', u' kB', u' MB', u' GB', u' TB']
429
430 try:
431 if type(_bytes) != int:
432 _bytes = int(_bytes, 10)
433 except:
434 return _bytes
435
436 for i in range(0, len(suffixes)):
437 if _bytes < 1024: break
438 _bytes /= 1024.0
439
440 if i:
441 return u'%.02f%s' % (_bytes, suffixes[i])
442 else:
443 return u'%d%s' % (_bytes, suffixes[i])
444
445def _toStr(v):
446 return v
447 if type(v) != unicode: return unicode(v)
448 else: return v
449
450def listToStr(l): return l #map(lambda v : _toStr(v), l)
451
452def generateHTMLLink(url, name=None, max_length=100, prefix=u'http'):
453 url = url
454 if not name: name = url
455 if not url.startswith(prefix): url = u'%s://%s' % (prefix, url)
456 return u'<a href="%s">%s</a>' % (url, name[:max_length])
457
458def createCurTitle(iwla, title):
459 title = iwla._(title)
460 month_name = time.strftime(u'%B', iwla.getCurTime())
461 year = time.strftime(u'%Y', iwla.getCurTime())
462 title += u' - %s %s' % (iwla._(month_name), year)
463 domain_name = iwla.getConfValue('domain_name', '')
464 if domain_name:
465 title += u' - %s' % (domain_name)
466 return title

Archive Download this file

Branches

Tags