123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- # Copyright (C) 2013 Google Inc. All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are
- # met:
- #
- # * Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- # * Redistributions in binary form must reproduce the above
- # copyright notice, this list of conditions and the following disclaimer
- # in the documentation and/or other materials provided with the
- # distribution.
- # * Neither the name of Google Inc. nor the names of its
- # contributors may be used to endorse or promote products derived from
- # this software without specific prior written permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- import calendar
- from datetime import datetime
- import itertools
- from time import time
- from google.appengine.ext import webapp
- from google.appengine.ext.webapp import template
- from config import logging, charts
- from model.patchlog import PatchLog
- from model.queues import Queue
- from model.queuelog import QueueLog
- class QueueCharts(webapp.RequestHandler):
- def get(self, queue_name):
- queue_name = queue_name.lower()
- if not Queue.queue_with_name(queue_name):
- self.error(404)
- return
- timestamp = self._get_timestamp()
- view_range = self._get_view_range()
- time_unit, time_unit_name = charts.get_time_unit(view_range)
- all_queue_names = map(Queue.name, Queue.all())
- template_values = {
- "all_queue_names": all_queue_names,
- "patch_data": self._get_patch_data(queue_name, timestamp, view_range),
- "queue_data": self._get_queue_data(queue_name, timestamp, view_range),
- "queue_name": queue_name,
- "seconds_ago_min": 0,
- "seconds_ago_max": view_range,
- "time_unit_name": time_unit_name,
- "time_unit": time_unit,
- "timestamp": timestamp,
- "view_range": view_range,
- "view_range_choices": charts.view_range_choices,
- }
- self.response.out.write(template.render("templates/queuecharts.html", template_values))
- @classmethod
- def _get_min_med_max(cls, values, defaults=(0, 0, 0)):
- if not values:
- return defaults
- length = len(values)
- sorted_values = sorted(values)
- return sorted_values[0], sorted_values[length / 2], sorted_values[length - 1]
- def _get_patch_data(self, queue_name, timestamp, view_range):
- patch_logs = self._get_patch_logs(queue_name, timestamp, view_range)
- patch_data = []
- for patch_log in patch_logs:
- if patch_log.process_duration and patch_log.wait_duration:
- patch_log_timestamp = calendar.timegm(patch_log.date.utctimetuple())
- patch_data.append({
- "attachment_id": patch_log.attachment_id,
- "seconds_ago": timestamp - patch_log_timestamp,
- "process_duration": patch_log.process_duration / charts.one_minute,
- "retry_count": patch_log.retry_count,
- "status_update_count": patch_log.status_update_count,
- "wait_duration": patch_log.wait_duration / charts.one_minute,
- })
- return patch_data
- def _get_patch_logs(self, queue_name, timestamp, view_range):
- patch_log_query = PatchLog.all()
- patch_log_query = patch_log_query.filter("queue_name =", queue_name)
- patch_log_query = patch_log_query.filter("date >=", datetime.utcfromtimestamp(timestamp - view_range))
- patch_log_query = patch_log_query.filter("date <=", datetime.utcfromtimestamp(timestamp))
- patch_log_query = patch_log_query.order("date")
- return patch_log_query.run(limit=charts.patch_log_limit)
- def _get_queue_data(self, queue_name, timestamp, view_range):
- queue_logs = self._get_queue_logs(queue_name, timestamp, view_range)
- queue_data = []
- for queue_log in queue_logs:
- queue_log_timestamp = calendar.timegm(queue_log.date.utctimetuple())
- p_min, p_med, p_max = self._get_min_med_max(queue_log.patch_process_durations)
- w_min, w_med, w_max = self._get_min_med_max(queue_log.patch_wait_durations)
- queue_data.append({
- "bots_seen": len(queue_log.bot_ids_seen),
- "seconds_ago": timestamp - queue_log_timestamp,
- "patch_processing_min": p_min,
- "patch_processing_med": p_med,
- "patch_processing_max": p_max,
- "patch_retry_count": queue_log.patch_retry_count,
- "patch_waiting_min": w_min,
- "patch_waiting_med": w_med,
- "patch_waiting_max": w_max,
- "patches_completed": len(queue_log.patch_process_durations),
- "patches_waiting": queue_log.max_patches_waiting,
- "status_update_count": queue_log.status_update_count,
- })
- return queue_data
- def _get_queue_logs(self, queue_name, timestamp, view_range):
- queue_logs = []
- current_timestamp = timestamp - view_range
- while current_timestamp <= timestamp:
- queue_logs.append(QueueLog.get_at(queue_name, logging.queue_log_duration, current_timestamp))
- current_timestamp += logging.queue_log_duration
- return queue_logs
- @classmethod
- def _get_time_unit(cls, view_range):
- if view_range > charts.one_day * 2:
- return
- def _get_timestamp(self):
- timestamp = self.request.get("timestamp")
- try:
- return int(timestamp)
- except ValueError:
- return int(time())
- def _get_view_range(self):
- view_range = self.request.get("view_range")
- try:
- return int(view_range)
- except ValueError:
- return charts.default_view_range
|