def velocity_chart(self): graph_width = 600 graph_height = 300 x_max = self.total_iterations y_max = max(max(self.estimate_stories_data()), max(self.estimate_tasks_data()), max(self.work_data())) chart = SimpleLineChart(graph_width, graph_height, x_range=(1, x_max + 1), y_range=(0, y_max + 1)) chart.add_data(self.estimate_stories_data()) chart.add_data(self.estimate_tasks_data()) chart.add_data(self.work_data()) chart.set_grid(0, 100.0 / y_max + 1, 5, 5) chart.set_colours(['FF0000', '00FF00', '0000FF']) chart.set_legend([_('rough story estimates'), _('task estimates'), _('worked')]) chart.set_legend_position('b') chart.set_axis_labels(Axis.LEFT, ['', '', _('days')]) chart.set_axis_labels(Axis.BOTTOM, range(1, x_max + 1)) chart.set_axis_range(Axis.LEFT, 0, y_max + 1) return chart.get_url(data_class=ExtendedData)
def get_elevation_chart_url(self, width, height): if len(self.positions) == 0: return '' int_elevations = [int(elevation) for elevation in ema([p.altitude for p in self.positions], 30)] max_elev = int(max(int_elevations) / 0.95) min_elev = int(min(int_elevations) * 0.95) chart = SimpleLineChart(width, height, y_range = (min_elev, max_elev)) chart.add_data(int_elevations) chart.set_axis_range(Axis.LEFT, min_elev, max_elev) return chart.get_url()
def many_labels(): chart = SimpleLineChart(settings.width, settings.height) for a in xrange(3): for axis_type in (Axis.LEFT, Axis.RIGHT, Axis.BOTTOM): index = chart.set_axis_range(axis_type, 0, random.random() * 100) chart.set_axis_style(index, colour=helper.random_colour(), \ font_size=random.random() * 10 + 5) chart.add_data(helper.random_data()) chart.download('label-many.png')
def many_labels(): chart = SimpleLineChart(settings.width, settings.height) for a in xrange(3): index = 0 for axis_type in (Axis.LEFT, Axis.RIGHT, Axis.BOTTOM): index = chart.set_axis_range(index, axis_type, 0, random.random() * 100) chart.set_axis_style(index, colour=helper.random_colour(), \ font_size=random.random() * 10 + 5) index += 1 chart.add_data(helper.random_data()) chart.download('label-many.png')
def _line_strip_graph(data, legends, axis_labels, size, steps, type=SimpleLineChart, multiline=False, **kwargs): if multiline: max_values = [] min_values = [] for row in data: max_values.append(max(row)) min_values.append(min(row)) max_y = max(max_values) min_y = min(min_values) else: max_y = max(data) min_y = min(data) #validando si hay datos para hacer grafico if max_y==0: return None chart = SimpleLineChart(size[0], size[1], y_range=[0, max_y*1.05]) if multiline: for row in data: chart.add_data(row) else: chart.add_data(data) step = ((max_y*1.05)-(min_y*0.95))/steps #validando en caso de el paso sea menor que uno y de cero en la conversion if step<1: step = 1 max_value = max_y tope = int(round(max_value*1.05)) if tope < max_value: tope+=2 else: tope+=1 try: left_axis = range(int(round(min_y*0.95)), tope, int(step)) except ValueError: #error por que los range no soportan decimales left_axis = range(0, 2) left_axis[0]='' chart.set_axis_range(Axis.LEFT, min_y, tope) if 'units' in kwargs: chart.set_axis_labels(Axis.LEFT, kwargs['units']) if 'time' in kwargs: chart.set_axis_labels(Axis.BOTTOM, kwargs['time']) chart.set_grid(0, 25, 4, 4,) chart.chls=4|4 #chart.fill_linear_stripes(Chart.CHART, 0, 'FFFFEF', 0.2, 'FFFFFF', 0.2) chart.set_colours(COLORS) if axis_labels: chart.set_axis_labels(Axis.BOTTOM, axis_labels) chart.set_legend(legends) chart.set_legend_position('b') if 'thickness' in kwargs: if multiline: for i in range(len(data)): chart.set_line_style(index = i, thickness=kwargs['thickness']) else: chart.set_line_style(index=0, thickness=kwargs['thickness']) return chart
def email_report(email, download_link, daily_report, weekly_report, host, port, login=None, password=None, dry_run=False, verbose=False): daily = [v[0] for k, v in daily_report.items()] if daily_report else [] weekly = [v[0] for k, v in weekly_report.items()] if weekly_report else [] cumulative_data = daily_report if daily_report else weekly_report if cumulative_data is None: raise Exception("No data given to generate a cumulative report!") cumulative = [v[1] for k, v in cumulative_data.items()] width, height = 700, 300 # Create the charts daily_chart = SimpleLineChart(width, height) weekly_chart = SimpleLineChart(width, height) cumulative_chart = SimpleLineChart(width, height) # Titles daily_chart.set_title('Daily Downloads') weekly_chart.set_title('Weekly Downloads') cumulative_chart.set_title('Cumulative Downloads') # Add data if daily: daily_chart.add_data(daily) daily_chart.set_axis_range(Axis.LEFT, 0, max(daily)) daily_chart.set_axis_labels(Axis.RIGHT, [min(daily), max(daily)]) if weekly: weekly_chart.add_data(weekly) weekly_chart.set_axis_range(Axis.LEFT, 0, max(weekly)) weekly_chart.set_axis_labels(Axis.RIGHT, [min(weekly), max(weekly)]) cumulative_chart.add_data(cumulative) cumulative_chart.set_axis_range(Axis.LEFT, 0, max(cumulative)) cumulative_chart.set_axis_labels( Axis.RIGHT, [min(cumulative), max(cumulative)]) # Set the styling marker = ('B', 'C5D4B5BB', '0', '0', '0') colors = ['3D7930', 'FF9900'] daily_chart.markers.append(marker) weekly_chart.markers.append(marker) cumulative_chart.markers.append(marker) daily_chart.set_colours(colors) weekly_chart.set_colours(colors) cumulative_chart.set_colours(colors) grid_args = 0, 10 grid_kwargs = dict(line_segment=2, blank_segment=6) daily_chart.set_grid(*grid_args, **grid_kwargs) weekly_chart.set_grid(*grid_args, **grid_kwargs) cumulative_chart.set_grid(*grid_args, **grid_kwargs) #daily_chart.fill_linear_stripes( # Chart.CHART, 0, 'CCCCCC', 0.2, 'FFFFFF', 0.2) daily_chart_url = daily_chart.get_url() if daily else None weekly_chart_url = weekly_chart.get_url() if weekly else None cumulative_chart_url = cumulative_chart.get_url() # Create recent versions of the charts if daily: recent_daily = daily[-90:] # Get last year's daily data. First, get the first date for the daily # data. start = daily_report.items()[-90][0] dt = datetime.datetime.strptime(start, '%Y/%m/%d') dt = dt - datetime.timedelta(weeks=52) last_year_datestr = datetime_to_str(dt) # Get the index in the data for the datestr try: i = daily_report.keys().index(last_year_datestr) recent_daily_comparison = daily[i:i + 90] except ValueError: recent_daily_comparison = [] if recent_daily_comparison: daily_chart.data = [recent_daily, recent_daily_comparison] else: daily_chart.data = [recent_daily] # Reset the axes daily_chart.axis = [] min_daily = min(recent_daily + recent_daily_comparison) max_daily = max(recent_daily + recent_daily_comparison) daily_chart.set_axis_range(Axis.LEFT, 0, max_daily) daily_chart.set_axis_labels(Axis.RIGHT, [min_daily, max_daily]) daily_chart.set_title('Recent Daily Downloads (filled is now)') daily_recent_chart_url = daily_chart.get_url() else: daily_recent_chart_url = None if verbose: print('Daily: ' + daily_chart_url) if daily_chart_url else None print('Weekly: ' + weekly_chart_url) if weekly_chart_url else None print('Cumulative: ' + cumulative_chart_url) print('Daily Recent: ' + daily_recent_chart_url) if daily_recent_chart_url else None # Create the body of the message (a plain-text and an HTML version). text = "Get an HTML mail client." html = """\ <html> <body> <h2>Latest download count: {latest_daily}</h2> <h2>Latest weekly download count: {latest_weekly}</h2> <h2>Latest cumulative total: {cumulative}</h2> <p><a href="{download}">Download today's report</a>.</p> <p><img src="cid:daily.png" width="{width}" height="{height}" alt="Daily Downloads" /></p> <p><img src="cid:weekly.png" width="{width}" height="{height}" alt="Weekly Downloads" /></p> <p><img src="cid:cumulative.png" width="{width}" height="{height}" alt="Cumulative Downloads" /></p> <p><img src="cid:daily-recent.png" width="{width}" height="{height}" alt="Recent Daily Downloads" /></p> </body> </html>""".format( latest_daily=daily[-1] if daily else 0, latest_weekly=weekly[-1] if weekly else 0, cumulative=cumulative[-1], download=download_link, width=width, height=height, ) # Create message container - the correct MIME type is multipart/alternative. message_root = MIMEMultipart('related') message_root['Subject'] = "Daily iTunes Download Report" message_root['From'] = email message_root['To'] = email message_root.preamble = 'This is a multi-part message.' # Record the MIME types of both parts - text/plain and text/html. alternative = MIMEMultipart('alternative') message_root.attach(alternative) part1 = MIMEText(text, 'plain') part2 = MIMEText(html, 'html') # Attach parts into message container. # According to RFC 2046, the last part of a multipart message, in this case # the HTML message, is best and preferred. alternative.attach(part1) alternative.attach(part2) # Get the images if daily_chart_url: r = requests.get(daily_chart_url) img = MIMEImage(r.content) img.add_header('Content-ID', '<daily.png>') message_root.attach(img) if weekly_chart_url: r = requests.get(weekly_chart_url) img = MIMEImage(r.content) img.add_header('Content-ID', '<weekly.png>') message_root.attach(img) r = requests.get(cumulative_chart_url) img = MIMEImage(r.content) img.add_header('Content-ID', '<cumulative.png>') message_root.attach(img) if daily_recent_chart_url: r = requests.get(daily_recent_chart_url) img = MIMEImage(r.content) img.add_header('Content-ID', '<daily-recent.png>') message_root.attach(img) try: # Send the message via local SMTP server. s = smtplib.SMTP(host, port) s.starttls() s.login(login, password) # sendmail function takes 3 arguments: sender's address, # recipient's address and message to send - here it is sent as one # string. s.sendmail(email, [email], message_root.as_string()) s.quit() except (ssl.SSLError, smtplib.SMTPServerDisconnected): print('Error') s.close()
from pygooglechart import SimpleLineChart, Axis chart = SimpleLineChart(200, 125) data = [ 1, 5, 30, 10, 25 ] chart.add_data(data) chart.set_axis_range(Axis.LEFT, 0, 10) chart.download("hello.png") print chart.get_url()
def render(self,data={}, context={}): assert self.series is not None, "'chart.series' must be set" converter = wfcommon.units.Converter(context["units"]) # merge builtin defaults, context and renderer config config = ChartConfig() if context.has_key('chart'): config.__dict__.update(context['chart']) config.__dict__.update(self.__dict__) # create the chart chart = SimpleLineChart(config.width, config.height) colors = [] legend_set = False legend = [] chart_min = sys.maxint chart_max = -sys.maxint # Prepare series config ordered_series = [] for key, serie in self.series.iteritems(): serie_config = ChartConfig() serie_config.__dict__.update(config.__dict__) serie_config.__dict__.update(serie) ordered_series.append( (serie_config.order, key, serie) ) ordered_series.sort( cmp=lambda x,y: cmp(x[0],y[0]) ) ordered_keys = [] for order, key, serie in ordered_series: ordered_keys.append(key) # Draws for each serie index=0 for order, key, serie in ordered_series: serie_config = ChartConfig() serie_config.__dict__.update(config.__dict__) serie_config.__dict__.update(serie) serie_data = copy.copy(data[key.split('.')[0]]['series'][key.split('.')[1]]) measure = key.split('.')[0] if flat(serie_data): # Series with all data = None continue if serie_config.accumulate: serie_data = accumulate(serie_data) elif serie_config.interpolate: serie_data = interpolate(serie_data) # Compute min and max value for the serie and the whole chart min_data = amin(serie_data) chart_min = rmin(chart_min, min_data) if serie_data.__contains__(min_data): min_index = serie_data.index(min_data) else: min_index = None max_data = max(serie_data) chart_max = max(chart_max, max_data) if serie_data.__contains__(max_data): max_index = serie_data.index(max_data) else: max_index = None (serie_data, min_index, max_index) = compress_to(serie_data, config.nval, min_index, max_index) chart.add_data(serie_data) colors.append(_valid_color(serie_config.color)) if serie_config.max and not max_index == None : max_config = ChartConfig() max_config.__dict__.update(serie_config.__dict__) max_config.__dict__.update(serie_config.max) str_max_data = str(round(converter.convert(measure, max_data), 1)) chart.add_marker(index, max_index, 't'+str_max_data, _valid_color(max_config.text), max_config.size) chart.add_marker(index, max_index, max_config.style, _valid_color(max_config.color), max_config.thickness) if serie_config.min and not min_index == None: min_config = ChartConfig() min_config.__dict__.update(serie_config.__dict__) min_config.__dict__.update(serie_config.min) str_min_data = str(round(converter.convert(measure, min_data), 1)) chart.add_marker(index, min_index, 't'+str_min_data, _valid_color(min_config.text), min_config.size) chart.add_marker(index, min_index, min_config.style, _valid_color(min_config.color), min_config.thickness) if serie_config.last: last_config = ChartConfig() last_config.__dict__.update(serie_config.__dict__) last_config.__dict__.update(serie_config.last) last_index=len(serie_data)-1 last_data = serie_data[last_index] if last_data: str_last_data = str(round(converter.convert(measure, last_data), 1)) chart.add_marker(index, last_index, 't'+str(last_data), _valid_color(last_config.text), last_config.size) chart.add_marker(index, last_index, last_config.style, _valid_color(last_config.color), last_config.thickness) if serie_config.area: fill_config = ChartConfig() fill_config.__dict__.update(serie_config.__dict__) fill_config.__dict__.update(serie_config.area) to = ordered_keys.index(fill_config.to) chart.add_fill_range(_valid_color(fill_config.color), index, to) if serie_config.dash: chart.set_line_style(index, serie_config.thickness, serie_config.dash, serie_config.dash) else: chart.set_line_style(index, serie_config.thickness) if serie_config.legend: legend.append(serie_config.legend) legend_set = True else: legend.append('') if serie_config.marks: mark_config = ChartConfig() mark_config.__dict__.update(serie_config.__dict__) mark_config.__dict__.update(serie_config.marks) mark_data = copy.copy(data[mark_config.serie.split('.')[0]]['series'][mark_config.serie.split('.')[1]]) mark_data = compress_to(mark_data, config.nval, min_index, max_index)[0] for i, m in enumerate(mark_data): if not m: mark_data[i] = " " density = max(1.0, 1.0 * mark_config.space * len("".join(mark_data))*mark_config.size / config.width) for i, v in enumerate(mark_data): if (i +1) % round(density) == 0: if serie_data[i] != 0: text = str(mark_data[i]) else: text = " " chart.add_marker(index, i, 't'+text, _valid_color(mark_config.color), mark_config.size) index = index + 1 # Compute vertical range if config.axes: range_min_ref_units = 0 if not chart_min == sys.maxint and not chart_max == -sys.maxint: range_min = chart_min-config.ymargin[0] range_max = chart_max+config.ymargin[1] range_min_target_units = math.floor(converter.convert(measure, range_min)) range_max_target_units = math.ceil(converter.convert(measure, range_max)) range_min_ref_units = converter.convert_back(measure, range_min_target_units) range_max_ref_units = converter.convert_back(measure, range_max_target_units) self.logger.debug("Y range: "+str(range_min_target_units) +" "+str(range_max_target_units)) chart.set_axis_range(Axis.LEFT, range_min_target_units, range_max_target_units+1) chart.add_data([range_min_ref_units, range_max_ref_units]) colors.append("00000000") else: chart.set_axis_range(Axis.LEFT, 0, 100) chart.set_axis_style(0, _valid_color(config.text), config.size, 0, Axis.BOTH if config.ticks else Axis.AXIS_LINES) else: chart.set_axis_labels(Axis.LEFT, []) chart.set_axis_style(0, _valid_color(config.text), config.size, 0, Axis.TICK_MARKS, _valid_color(config.bgcolor)) if config.zero and config.axes and range_min_ref_units < 0 and range_max_ref_units > 0: zero_config = ChartConfig() zero_config.__dict__.update(config.__dict__) zero_config.__dict__.update(config.zero) chart.add_data([0]*2) colors.append(_valid_color(zero_config.color)) chart.set_line_style(index, zero_config.thickness) chart.set_colours(colors) chart.fill_solid(Chart.BACKGROUND, _valid_color(config.bgcolor)) if legend_set: chart.set_legend(legend) chart.set_legend_position(config.legend_pos) if self.labels: labels_data = copy.copy(data[self.labels.split('.')[0]]['series'][self.labels.split('.')[1]]) labels_data = compress_to(labels_data, config.nval, None, None)[0] if config.axes: density = 1.0 * len("".join(labels_data))*config.size / config.width if density > LABEL_DENSITY_THRESHOLD: for i, v in enumerate(labels_data): if i % round(density) != 0: labels_data[i] = ' ' chart.set_axis_labels(Axis.BOTTOM, labels_data) chart.set_axis_style(1, _valid_color(config.text), config.size, 0, Axis.BOTH if config.ticks else Axis.AXIS_LINES) else: chart.set_axis_labels(Axis.BOTTOM, []) chart.set_axis_style(1, _valid_color(config.text), config.size, 0, Axis.TICK_MARKS, _valid_color(config.color)) try: return chart.get_url()+"&chma=10,10,10,10" # add a margin except: self.logger.exception("Could not render chart") return "http://chart.apis.google.com/chart?cht=lc&chs="+str(config.width)+"x"+str(config.height)
def _line_strip_graph(data, legends, axis_labels, size, steps, type=SimpleLineChart, multiline=False, **kwargs): if multiline: max_values = [] min_values = [] for row in data: max_values.append(max(row)) min_values.append(min(row)) max_y = max(max_values) min_y = min(min_values) else: max_y = max(data) min_y = min(data) #validando si hay datos para hacer grafico if max_y == 0: return None chart = SimpleLineChart(size[0], size[1], y_range=[0, max_y * 1.05]) if multiline: for row in data: chart.add_data(row) else: chart.add_data(data) step = ((max_y * 1.05) - (min_y * 0.95)) / steps #validando en caso de el paso sea menor que uno y de cero en la conversion if step < 1: step = 1 max_value = max_y tope = int(round(max_value * 1.05)) if tope < max_value: tope += 2 else: tope += 1 try: left_axis = range(int(round(min_y * 0.95)), tope, int(step)) except ValueError: #error por que los range no soportan decimales left_axis = range(0, 2) left_axis[0] = '' chart.set_axis_range(Axis.LEFT, min_y, tope) if 'units' in kwargs: chart.set_axis_labels(Axis.LEFT, kwargs['units']) if 'time' in kwargs: chart.set_axis_labels(Axis.BOTTOM, kwargs['time']) chart.set_grid( 0, 25, 4, 4, ) chart.chls = 4 | 4 #chart.fill_linear_stripes(Chart.CHART, 0, 'FFFFEF', 0.2, 'FFFFFF', 0.2) chart.set_colours(COLORS) if axis_labels: chart.set_axis_labels(Axis.BOTTOM, axis_labels) chart.set_legend(legends) chart.set_legend_position('b') if 'thickness' in kwargs: if multiline: for i in range(len(data)): chart.set_line_style(index=i, thickness=kwargs['thickness']) else: chart.set_line_style(index=0, thickness=kwargs['thickness']) return chart
def lambda_handler(event, context): # http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html url = "http://weather.olp.yahooapis.jp/v1/place" payload = {'appid': YAHOO_APP_ID, 'coordinates': LON + "," + LAT, 'output': 'json'} result = requests.get(url, params=payload) timeString = getTimeString(30) send_message = False json_data = json.loads(result.content) rainfall = json_data['Feature'][0]['Property']['WeatherList']['Weather']#[3]['Rainfall'] dynamodb = boto3.resource('dynamodb') table = dynamodb.Table(inifile.get('dynamodb', 'table_id')) key_id = int(inifile.get('dynamodb', 'id_value')) result = table.get_item( Key={ 'Id': key_id } ) if 'raining' not in result: table.put_item( Item={ "Id":key_id, "raining": False, "source": inifile.get('dynamodb', 'source')} ) raining = result['Item']['raining'] if rainfall[3]['Rainfall'] >= ALERT_THRESH and raining == False: send_message = True message = timeString + "で" + str(rainfall[3]['Rainfall']) + "mm/hの雨が予想されます。" elif rainfall[3]['Rainfall'] < ALERT_THRESH and raining == True: send_message = True message = timeString + "で" + "雨が止む予報です。" elif rainfall[3]['Rainfall'] >= ALERT_THRESH and raining == True: send_message = True message = timeString + "で" + str(rainfall[3]['Rainfall']) + "mm/hの雨が引き続き降る予報です。" elif rainfall[3]['Rainfall'] < ALERT_THRESH and raining == False: send_message = False if rainfall[3]['Rainfall'] >= ALERT_THRESH: table.put_item( Item={ "Id":key_id, "raining": True, "source": inifile.get('dynamodb', 'source')} ) else: table.put_item( Item={ "Id":key_id, "raining": False, "source": inifile.get('dynamodb', 'source')} ) if send_message == True: width = 500 height = 150 chart = SimpleLineChart(width, height) chart.set_colours(['719CFF']) rainfall_array = [rainfall[0]['Rainfall'], rainfall[1]['Rainfall'], rainfall[2]['Rainfall'], rainfall[3]['Rainfall'], rainfall[4]['Rainfall'], rainfall[5]['Rainfall'], rainfall[6]['Rainfall']] chart.add_data(rainfall_array) chart.set_axis_labels(Axis.BOTTOM, [getTimeString(0), getTimeString(10), getTimeString(20), getTimeString(30), getTimeString(40), getTimeString(50), getTimeString(60)]) chart.set_legend(['Rainfall(mm/h)']) chart.set_legend_position('bv') if math.ceil(max(rainfall_array)) < 1.0: chart.y_range=(0, 1) chart.set_axis_range(Axis.LEFT, 0, 1) else: chart.y_range=(0, math.ceil(max(rainfall_array))) chart.set_axis_range(Axis.LEFT, 0, math.ceil(max(rainfall_array))) d = datetime.now() + timedelta(hours=9, minutes=30) date_str = d.strftime("|date:%Y%m%d%H%M|datelabel:on") radar_url = "http://weather.yahoo.co.jp/weather/zoomradar/?lat=" + LAT + "&lon=" + LON + "&z=12" rainfall_image_url = getRainfallRadarUrl(LAT, LON, ZOOM, MAP_IMAGE_X, MAP_IMAGE_Y) + date_str chart_url = shorten_url(chart.get_url()) rainfall_image_url = shorten_url(rainfall_image_url) radar_url = shorten_url(radar_url) message = unicode(message, 'utf-8') + "\n" + chart_url + "\n" + timeString + u"の雨雲予想: " + rainfall_image_url + "\n" + u"詳細: " + radar_url + "\n" sc = SlackClient(inifile.get('slack', 'token')) res = sc.api_call("chat.postMessage", channel=inifile.get('slack', 'channel'), text=message, username=inifile.get('slack', 'username'), icon_emoji=inifile.get('slack', 'icon_emoji')) print(res)
def render(self, data={}, context={}): assert self.series is not None, "'chart.series' must be set" converter = wfcommon.units.Converter(context["units"]) # merge builtin defaults, context and renderer config config = ChartConfig() if context.has_key('chart'): config.__dict__.update(context['chart']) config.__dict__.update(self.__dict__) # create the chart chart = SimpleLineChart(config.width, config.height) colors = [] legend_set = False legend = [] chart_min = sys.maxint chart_max = -sys.maxint # Prepare series config ordered_series = [] for key, serie in self.series.iteritems(): serie_config = ChartConfig() serie_config.__dict__.update(config.__dict__) serie_config.__dict__.update(serie) ordered_series.append((serie_config.order, key, serie)) ordered_series.sort(cmp=lambda x, y: cmp(x[0], y[0])) ordered_keys = [] for order, key, serie in ordered_series: ordered_keys.append(key) # Draws for each serie index = 0 for order, key, serie in ordered_series: serie_config = ChartConfig() serie_config.__dict__.update(config.__dict__) serie_config.__dict__.update(serie) serie_data = copy.copy( data[key.split('.')[0]]['series'][key.split('.')[1]]) measure = key.split('.')[0] if flat(serie_data): # Series with all data = None continue if serie_config.accumulate: serie_data = accumulate(serie_data) elif serie_config.interpolate: serie_data = interpolate(serie_data) # Compute min and max value for the serie and the whole chart min_data = amin(serie_data) chart_min = rmin(chart_min, min_data) if serie_data.__contains__(min_data): min_index = serie_data.index(min_data) else: min_index = None max_data = max(serie_data) chart_max = max(chart_max, max_data) if serie_data.__contains__(max_data): max_index = serie_data.index(max_data) else: max_index = None (serie_data, min_index, max_index) = compress_to(serie_data, config.nval, min_index, max_index) chart.add_data(serie_data) colors.append(_valid_color(serie_config.color)) if serie_config.max and not max_index == None: max_config = ChartConfig() max_config.__dict__.update(serie_config.__dict__) max_config.__dict__.update(serie_config.max) str_max_data = str( round(converter.convert(measure, max_data), 1)) chart.add_marker(index, max_index, 't' + str_max_data, _valid_color(max_config.text), max_config.size) chart.add_marker(index, max_index, max_config.style, _valid_color(max_config.color), max_config.thickness) if serie_config.min and not min_index == None: min_config = ChartConfig() min_config.__dict__.update(serie_config.__dict__) min_config.__dict__.update(serie_config.min) str_min_data = str( round(converter.convert(measure, min_data), 1)) chart.add_marker(index, min_index, 't' + str_min_data, _valid_color(min_config.text), min_config.size) chart.add_marker(index, min_index, min_config.style, _valid_color(min_config.color), min_config.thickness) if serie_config.last: last_config = ChartConfig() last_config.__dict__.update(serie_config.__dict__) last_config.__dict__.update(serie_config.last) last_index = len(serie_data) - 1 last_data = serie_data[last_index] if last_data: str_last_data = str( round(converter.convert(measure, last_data), 1)) chart.add_marker(index, last_index, 't' + str(last_data), _valid_color(last_config.text), last_config.size) chart.add_marker(index, last_index, last_config.style, _valid_color(last_config.color), last_config.thickness) if serie_config.area: fill_config = ChartConfig() fill_config.__dict__.update(serie_config.__dict__) fill_config.__dict__.update(serie_config.area) to = ordered_keys.index(fill_config.to) chart.add_fill_range(_valid_color(fill_config.color), index, to) if serie_config.dash: chart.set_line_style(index, serie_config.thickness, serie_config.dash, serie_config.dash) else: chart.set_line_style(index, serie_config.thickness) if serie_config.legend: legend.append(serie_config.legend) legend_set = True else: legend.append('') if serie_config.marks: mark_config = ChartConfig() mark_config.__dict__.update(serie_config.__dict__) mark_config.__dict__.update(serie_config.marks) mark_data = copy.copy(data[mark_config.serie.split( '.')[0]]['series'][mark_config.serie.split('.')[1]]) mark_data = compress_to(mark_data, config.nval, min_index, max_index)[0] for i, m in enumerate(mark_data): if not m: mark_data[i] = " " density = max( 1.0, 1.0 * mark_config.space * len("".join(mark_data)) * mark_config.size / config.width) for i, v in enumerate(mark_data): if (i + 1) % round(density) == 0: if serie_data[i] != 0: text = str(mark_data[i]) else: text = " " chart.add_marker(index, i, 't' + text, _valid_color(mark_config.color), mark_config.size) index = index + 1 # Compute vertical range if config.axes: range_min_ref_units = 0 if not chart_min == sys.maxint and not chart_max == -sys.maxint: range_min = chart_min - config.ymargin[0] range_max = chart_max + config.ymargin[1] range_min_target_units = math.floor( converter.convert(measure, range_min)) range_max_target_units = math.ceil( converter.convert(measure, range_max)) range_min_ref_units = converter.convert_back( measure, range_min_target_units) range_max_ref_units = converter.convert_back( measure, range_max_target_units) self.logger.debug("Y range: " + str(range_min_target_units) + " " + str(range_max_target_units)) chart.set_axis_range(Axis.LEFT, range_min_target_units, range_max_target_units + 1) chart.add_data([range_min_ref_units, range_max_ref_units]) colors.append("00000000") else: chart.set_axis_range(Axis.LEFT, 0, 100) chart.set_axis_style( 0, _valid_color(config.text), config.size, 0, Axis.BOTH if config.ticks else Axis.AXIS_LINES) else: chart.set_axis_labels(Axis.LEFT, []) chart.set_axis_style(0, _valid_color(config.text), config.size, 0, Axis.TICK_MARKS, _valid_color(config.bgcolor)) if config.zero and config.axes and range_min_ref_units < 0 and range_max_ref_units > 0: zero_config = ChartConfig() zero_config.__dict__.update(config.__dict__) zero_config.__dict__.update(config.zero) chart.add_data([0] * 2) colors.append(_valid_color(zero_config.color)) chart.set_line_style(index, zero_config.thickness) chart.set_colours(colors) chart.fill_solid(Chart.BACKGROUND, _valid_color(config.bgcolor)) if legend_set: chart.set_legend(legend) chart.set_legend_position(config.legend_pos) if self.labels: labels_data = copy.copy(data[self.labels.split('.')[0]]['series'][ self.labels.split('.')[1]]) labels_data = compress_to(labels_data, config.nval, None, None)[0] if config.axes: density = 1.0 * len( "".join(labels_data)) * config.size / config.width if density > LABEL_DENSITY_THRESHOLD: for i, v in enumerate(labels_data): if i % round(density) != 0: labels_data[i] = ' ' chart.set_axis_labels(Axis.BOTTOM, labels_data) chart.set_axis_style( 1, _valid_color(config.text), config.size, 0, Axis.BOTH if config.ticks else Axis.AXIS_LINES) else: chart.set_axis_labels(Axis.BOTTOM, []) chart.set_axis_style(1, _valid_color(config.text), config.size, 0, Axis.TICK_MARKS, _valid_color(config.color)) try: return chart.get_url() + "&chma=10,10,10,10" # add a margin except: self.logger.exception("Could not render chart") return "http://chart.apis.google.com/chart?cht=lc&chs=" + str( config.width) + "x" + str(config.height)
def email_report(email, download_link, daily_report, weekly_report, host, port, login=None, password=None, dry_run=False, verbose=False): daily = [v[0] for k, v in daily_report.items()] if daily_report else [] daily_updates = daily_report.items()[-1][1][1] daily_edu = daily_report.items()[-1][1][2] weekly = [v[0] for k, v in weekly_report.items()] if weekly_report else [] cumulative_data = daily_report if daily_report else weekly_report if cumulative_data is None: raise Exception("No data given to generate a cumulative report!") cumulative = [v[3] for k, v in cumulative_data.items()] width, height = 700, 300 # Create the charts daily_chart = SimpleLineChart(width, height) weekly_chart = SimpleLineChart(width, height) cumulative_chart = SimpleLineChart(width, height) # Titles daily_chart.set_title('Daily Downloads') weekly_chart.set_title('Weekly Downloads') cumulative_chart.set_title('Cumulative Downloads') # Add data if daily: daily_data = daily[-180:] daily_chart.add_data(daily_data) daily_chart.set_axis_range(Axis.LEFT, 0, max(daily_data)) daily_chart.set_axis_labels(Axis.RIGHT, [min(daily_data), max(daily_data)]) if weekly: weekly_chart.add_data(weekly) weekly_chart.set_axis_range(Axis.LEFT, 0, max(weekly)) weekly_chart.set_axis_labels(Axis.RIGHT, [min(weekly), max(weekly)]) cumulative_chart.add_data(cumulative) cumulative_chart.set_axis_range(Axis.LEFT, 0, max(cumulative)) cumulative_chart.set_axis_labels( Axis.RIGHT, [min(cumulative), max(cumulative)]) # Set the styling marker = ('B', 'C5D4B5BB', '0', '0', '0') colors = ['3D7930', 'FF9900'] daily_chart.markers.append(marker) weekly_chart.markers.append(marker) cumulative_chart.markers.append(marker) daily_chart.set_colours(colors) weekly_chart.set_colours(colors) cumulative_chart.set_colours(colors) grid_args = 0, 10 grid_kwargs = dict(line_segment=2, blank_segment=6) daily_chart.set_grid(*grid_args, **grid_kwargs) weekly_chart.set_grid(*grid_args, **grid_kwargs) cumulative_chart.set_grid(*grid_args, **grid_kwargs) #daily_chart.fill_linear_stripes( # Chart.CHART, 0, 'CCCCCC', 0.2, 'FFFFFF', 0.2) daily_chart_url = daily_chart.get_url() if daily else None weekly_chart_url = weekly_chart.get_url() if weekly else None cumulative_chart_url = cumulative_chart.get_url() # Create recent versions of the charts if daily: recent_daily = daily[-90:] # Get last year's daily data. First, get the first date for the daily # data. start = daily_report.items()[-90][0] dt = datetime.datetime.strptime(start, '%Y/%m/%d') dt = dt - datetime.timedelta(weeks=52) last_year_datestr = datetime_to_str(dt) # Get the index in the data for the datestr try: i = daily_report.keys().index(last_year_datestr) recent_daily_comparison = daily[i:i + 90] except ValueError: recent_daily_comparison = [] if recent_daily_comparison: daily_chart.data = [recent_daily, recent_daily_comparison] else: daily_chart.data = [recent_daily] # Reset the axes daily_chart.axis = [] min_daily = min(recent_daily + recent_daily_comparison) max_daily = max(recent_daily + recent_daily_comparison) daily_chart.set_axis_range(Axis.LEFT, 0, max_daily) daily_chart.set_axis_labels(Axis.RIGHT, [min_daily, max_daily]) daily_chart.set_title('Recent Daily Downloads (filled is now)') daily_recent_chart_url = daily_chart.get_url() else: daily_recent_chart_url = None if verbose: print('Daily: ' + daily_chart_url) if daily_chart_url else None print('Weekly: ' + weekly_chart_url) if weekly_chart_url else None print('Cumulative: ' + cumulative_chart_url) print('Daily Recent: ' + daily_recent_chart_url) if daily_recent_chart_url else None yesterday = datetime.date.today() - datetime.timedelta(days=1) # Create the body of the message (a plain-text and an HTML version). text = "Get an HTML mail client." html = """\ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=600" /> <style> @media only screen and (min-device-width: 541px) {{ .content {{ width: 540px !important; }} }} </style> </head> <body> <table class="content" align="center" cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width: 540px; border: 1px solid #cbcbcb;"> <tr> <td height="32" style="text-align: center; background-color:#fe8359;height:32px;color:#fff" bgcolor="fe8359"> <span style="font-size:11px">Daily Report for</span> <span style="font-size:14px;font-weight:bold">{yesterday:%A, %B %d, %Y}</span> </td> </tr> <tr> <td height="30" valign="middle"> <table cellpadding="0" cellspacing="0" border="0" style="width: 100%; text-align: center"> <tr> <td style="margin-top: 5px;"> <span style="font-size: 10px; color: #777">Downloads:</span> <div style="font-size: 15px; margin-top: 3px; "><b>{latest_daily:,}</b></div> </td> <td style="margin-top: 5px;"> <span style="font-size: 10px; color: #777">Updates:</span> <div style="font-size: 15px; margin-top: 3px; "><b>{latest_updates:,}</b></div> </td> <td style="margin-top: 5px;"> <span style="font-size: 10px; color: #777">Educational:</span> <div style="font-size: 15px; margin-top: 3px; "><b>{latest_edu:,}</b></div> </td> <td style="margin-top: 5px;"> <span style="font-size: 10px; color: #777">Since Monday:</span> <div style="font-size: 15px; margin-top: 3px; "><b>{latest_weekly:,}</b></div> </td> <td style="margin-top: 5px;"> <span style="font-size: 10px; color: #777">Cumulative:</span> <div style="font-size: 15px; margin-top: 3px; "><b>{cumulative:,}</b></div> </td> </tr> </table> </td> </tr> <tr> <td></td> </tr> <tr> <td><hr color="#cbcbcb"></td> </tr> <tr><td style="padding: 5px;"><img src="cid:daily.png" width="{width}" height="{height}" alt="Daily Downloads" /></td></tr> <tr><td style="padding: 5px;"><img src="cid:weekly.png" width="{width}" height="{height}" alt="Weekly Downloads" /></td></tr> <tr><td style="padding: 5px;"><img src="cid:cumulative.png" width="{width}" height="{height}" alt="Cumulative Downloads" /></td></tr> <tr><td style="padding: 5px;"><img src="cid:daily-recent.png" width="{width}" height="{height}" alt="Recent Daily Downloads" /></td></tr> <tr> <td><hr color="#cbcbcb"></td> </tr> <tr> <td style="padding: 10px; text-align: center;"> <a style="color: #777" href="{download}">Download today's report</a> </td> </tr> </table> </body> </html>""".format( yesterday=yesterday, latest_daily=daily[-1] if daily else 0, latest_weekly=weekly[-1] if weekly else 0, latest_updates=daily_updates, latest_edu=daily_edu, cumulative=cumulative[-1], download=download_link, width=width, height=height, ) # Create message container - the correct MIME type is multipart/alternative. message_root = MIMEMultipart('related') message_root['Subject'] = "iTunes Report for {:%A, %B %d, %Y}".format(yesterday) message_root['From'] = email message_root['To'] = email message_root.preamble = 'This is a multi-part message.' # Record the MIME types of both parts - text/plain and text/html. alternative = MIMEMultipart('alternative') message_root.attach(alternative) part1 = MIMEText(text, 'plain') part2 = MIMEText(html, 'html') # Attach parts into message container. # According to RFC 2046, the last part of a multipart message, in this case # the HTML message, is best and preferred. alternative.attach(part1) alternative.attach(part2) # Get the images if daily_chart_url: r = requests.get(daily_chart_url) img = MIMEImage(r.content, _subtype='png') img.add_header('Content-ID', '<daily.png>') message_root.attach(img) if weekly_chart_url: r = requests.get(weekly_chart_url) img = MIMEImage(r.content, _subtype='png') img.add_header('Content-ID', '<weekly.png>') message_root.attach(img) r = requests.get(cumulative_chart_url) img = MIMEImage(r.content, _subtype='png') img.add_header('Content-ID', '<cumulative.png>') message_root.attach(img) if daily_recent_chart_url: r = requests.get(daily_recent_chart_url) img = MIMEImage(r.content, _subtype='png') img.add_header('Content-ID', '<daily-recent.png>') message_root.attach(img) try: # Send the message via local SMTP server. s = smtplib.SMTP(host, port) s.starttls() s.login(login, password) # sendmail function takes 3 arguments: sender's address, # recipient's address and message to send - here it is sent as one # string. s.sendmail(email, [email], message_root.as_string()) s.quit() except (ssl.SSLError, smtplib.SMTPServerDisconnected): print('Error') s.close()