Skip to content
This repository has been archived by the owner on Jun 13, 2020. It is now read-only.

Add option to draw 'absolute' data set (not percentage) - #37

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions bin/draw_stack_bars
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ def drawStackBars(series, options):
stacked_bars.render(series)
stacked_bars.save(options.get('output_file'))

def setDefaultColors(options):
#import pdb;pdb.set_trace()
if options.draw_mode == "percentage":
options.full_color = options.full_color or '#00e600'
options.empty_color = options.empty_color or '#FF0000'
elif options.draw_mode == "absolute":
options.full_color = options.full_color or '#3333ff'
return options

def parse_options():
usage = "usage: %prog [options] label=value label=value"

Expand All @@ -60,11 +69,31 @@ def parse_options():
help="Width of the border around the bars",
dest="bar_border", default=1)

parser.add_option("-m", "--mode", type="choice",
help="Drawing mode : percentage, absolute",
choices = ['percentage', 'absolute'],
dest="draw_mode", default="percentage")

parser.add_option("--full-color", type="string",
help="Color for full bars (eg. #00e600)",
dest="full_color")

parser.add_option("--empty-color", type="string",
help="Color for empty bars (eg. #00e600)",
dest="empty_color")

parser.add_option("--font", type="choice",
help="Font to use",
choices = ['aerial', 'arial_black', 'Tahoma'],
dest="font_name", default = "arial_black")


options, data = parser.parse_args()
if not data:
sys.stderr.write("No data to plot !\n\n")
parser.print_help()
sys.exit(1)
options = setDefaultColors(options)
return options, data

if __name__=='__main__':
Expand Down
50 changes: 42 additions & 8 deletions lib/stackbars/grouped_stackbars.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ def __init__(self, options = {}):
self.bar_height_ratio = options.get('bar_height_ratio', .2)
assert self.bar_height_ratio <=1, "bar_height_ratio must be <= 1"

self.font_type = options.get('font_type', get_font('arial_black.ttf'))
font_name = "%s.ttf" % options.get('font_name', 'arial_black')
self.font_type = get_font(font_name)
self.min_font_size = 15

self.img = None
self.draw = None

self.stack_bars = []
self.draw_mode = options.get('draw_mode', 'percentage')

def initializeDims(self, data, optimize_bar_area_width = True):
self.bars_width = int(self.bar_area_ratio * self.width)
Expand All @@ -37,7 +39,7 @@ def initializeDims(self, data, optimize_bar_area_width = True):
optimal_font_size = get_optimal_font_size(
self.draw,
self.font_type,
self.width -self.bars_width,
self.width - self.bars_width,
self.bar_height,
longest_label,
self.min_font_size
Expand Down Expand Up @@ -66,26 +68,58 @@ def get_longest_label(self, data):
def initiliazeStackBars(self, data):
self.stack_bars = []
# get the dimensions for the gauges area and for the text area
for label, value in data:
sb = Stackbar(self.draw, label, value, self.options)
for label, value, fill_value in data:
sb = Stackbar(self.draw, label, value, fill_value, self.options)
self.stack_bars.append(sb)

def renderStackBars(self):
offset = 0
for i, sb in enumerate(self.stack_bars):
draw_bottom_border = i == (len(self.stack_bars) - 1)
sb.render(self.width, offset, self.bars_width, self.bar_height, self.font_size, draw_bottom_border)
if self.draw_mode == "percentage":
sb.render_percentage(self.width, offset, self.bars_width, self.bar_height, self.font_size, draw_bottom_border)
elif self.draw_mode == "absolute":
sb.render_absolute(self.width, offset, self.bars_width, self.bar_height, self.font_size, draw_bottom_border)
offset += self.bar_height

# Format the data
# to always make sure the max value is 100
# in an absolute mode
# in percentage mode, raise an exception if the value > 100
# example input : [('ios-boe-aatp-tests', 180), ('android-boe-aatp-tests', 10)]
# output : [('ios-boe-aatp-tests', 180, 100), ('android-boe-aatp-tests', 10, 10 * 100 / 180)]
def format_data(self, data):
# Find the max input
max_val = 0
formatted_data = []

for k,v in data:
if self.draw_mode == "percentage":
assert v <= 100, "percentage draw mode : invalid fill value for %s - must be <= 100" %k
elif self.draw_mode == "absolute":
max_val = max(max_val, v)

for k,v in data:
value = v
fill_value = v
if self.draw_mode == "percentage":
value = "%i%%" % value
elif self.draw_mode == "absolute":
fill_value = v * 100. / max_val

formatted_data.append([k,str(value),fill_value])
return formatted_data

def render(self, data):
if not data:
formatted_data = self.format_data(data)
if not formatted_data:
return

# Initialize the drawing area
self.initializeDims(data)
self.initializeDims(formatted_data)

# Create new stack bars
self.initiliazeStackBars(data)
self.initiliazeStackBars(formatted_data)

# render each gauge according to the optimal dims
self.renderStackBars()
Expand Down
37 changes: 25 additions & 12 deletions lib/stackbars/stackbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
from chart_utils import get_optimal_font_size, get_font

class Stackbar:
def __init__(self, draw, legend, fill_value, options = {}):

assert fill_value <= 100, "Invalid fill value - must be <= 100"

def __init__(self, draw, legend, data_value, fill_value, options = {}):
self.legend = legend
self.data_value = data_value
self.fill_value = fill_value

self.border = options.get('bar_border', 1)
self.full_color = options.get('green', (255, 0, 0))
self.empty_color = options.get('red', (0, 255, 0))
self.font_type = options.get('font_type', get_font('arial_black.ttf'))
self.full_color = options.get('full_color', (255, 0, 0))
self.empty_color = options.get('empty_color', (0, 255, 0))

font_name = "%s.ttf" % options.get('font_name', 'arial_black')
self.font_type = get_font(font_name)

self.light_font_color = options.get('fontcolor', (255, 255, 255))
self.dark_font_color = options.get('fontcolor', (0, 0, 0))
Expand All @@ -29,18 +29,17 @@ def drawFullRectangle(self, width, bar_height, height_offset, font_size, bottom_
self.draw.rectangle((full_top_left, full_bottom_right), fill=self.full_color)

# Draw text
rect_label = "%i%%" % self.fill_value
optimal_font_size = get_optimal_font_size(self.draw, self.font_type, rect_width, bar_height, rect_label, 7, font_size)
optimal_font_size = get_optimal_font_size(self.draw, self.font_type, rect_width, bar_height, self.data_value, 7, font_size)
if not optimal_font_size:
return

font = ImageFont.truetype(self.font_type, optimal_font_size)
label_width, label_height = self.draw.textsize(rect_label, font=font)
label_width, label_height = self.draw.textsize(self.data_value, font=font)
label_margin = (rect_width - label_width) / 2
label_offset = (bar_height - label_height) / 2
self.draw.text(
(self.border + 1 + label_margin, height_offset + self.border + 1 + label_offset),
rect_label,
self.data_value,
font=font,
fill=self.light_font_color
)
Expand Down Expand Up @@ -85,7 +84,7 @@ def drawLegend(self, width_offset, bar_height, height_offset, font_size):
def drawBase(self, width, bar_height, height_offset):
self.draw.rectangle((0,height_offset, width, height_offset + bar_height), fill=(0,0,0))

def render(self, width, height_offset, bars_width, bar_height, font_size, draw_bottom_border = True):
def render_percentage(self, width, height_offset, bars_width, bar_height, font_size, draw_bottom_border = True):
if draw_bottom_border:
bottom_border_offset = 1
else:
Expand All @@ -98,3 +97,17 @@ def render(self, width, height_offset, bars_width, bar_height, font_size, draw_b
self.drawEmptyRectangle(bars_width, bar_height, height_offset, font_size - 1, bottom_border_offset)

self.drawLegend(bars_width, bar_height, height_offset, font_size)

def render_absolute(self, width, height_offset, bars_width, bar_height, font_size, draw_bottom_border = True):
if draw_bottom_border:
bottom_border_offset = 1
else:
bottom_border_offset = 0

#self.drawBase(bars_width, bar_height, height_offset)

self.drawFullRectangle(bars_width, bar_height, height_offset, font_size - 1, bottom_border_offset)

#self.drawEmptyRectangle(bars_width, bar_height, height_offset, font_size - 1, bottom_border_offset)

self.drawLegend(bars_width, bar_height, height_offset, font_size)