def get(self): extra = frozenset(self.request.GET) - self.EXPECTED if extra: self.abort_with_error( 400, error='Extraneous query parameters. Did you make a typo? %s' % ','.join(sorted(extra))) # Use a similar query to /user/tasks. name = self.request.get('name') tags = self.request.get_all('tag') cursor_str = self.request.get('cursor') limit = int(self.request.get('limit', 100)) sort = self.request.get('sort', 'created_ts') state = self.request.get('state', 'all') uses = bool(name) + bool(tags) + bool(state!='all') if uses > 1: self.abort_with_error( 400, error='Only one of name, tag (1 or many) or state can be used') items, cursor_str, sort, state = task_result.get_tasks( name, tags, cursor_str, limit, sort, state) data = { 'cursor': cursor_str, 'items': items, 'limit': limit, 'sort': sort, 'state': state, } self.send_response(utils.to_json_encodable(data))
def list(self, request): """Provides a list of available tasks.""" state = request.state.name.lower() uses = sum([ request.name is not None, bool(request.tag), state != 'all']) if uses > 1: raise endpoints.BadRequestException( 'Only one of name, tag (1 or many) or state can be used.') # get the tasks items, cursor_str, sort, state = task_result.get_tasks( request.name, request.tag, request.cursor, request.limit, request.sort, state) return swarming_rpcs.TaskList( cursor=cursor_str, items=[message_conversion.task_result_summary_from_dict( utils.to_json_encodable(item)) for item in items], limit=request.limit, sort=sort, state=state)
def list(self, request): """Provides a list of available tasks.""" state = request.state.name.lower() uses = sum( [request.name is not None, bool(request.tag), state != 'all']) if uses > 1: raise endpoints.BadRequestException( 'Only one of name, tag (1 or many) or state can be used.') # get the tasks items, cursor_str, sort, state = task_result.get_tasks( request.name, request.tag, request.cursor, request.limit, request.sort, state) return swarming_rpcs.TaskList( cursor=cursor_str, items=[ message_conversion.task_result_summary_from_dict( utils.to_json_encodable(item)) for item in items ], limit=request.limit, sort=sort, state=state)
def get(self): """Handles both ndb.Query searches and search.Index().search() queries. If |task_name| is set or not affects the meaning of |cursor|. When set, the cursor is for search.Index, otherwise the cursor is for a ndb.Query. """ cursor_str = self.request.get('cursor') limit = int(self.request.get('limit', 100)) sort = self.request.get('sort', self.SORT_CHOICES[0][0]) state = self.request.get('state', self.STATE_CHOICES[0][0][0]) task_name = self.request.get('task_name', '').strip() task_tags = [ line for line in self.request.get('task_tag', '').splitlines() if line ] if not any(sort == i[0] for i in self.SORT_CHOICES): self.abort(400, 'Invalid sort') if not any(any(state == i[0] for i in j) for j in self.STATE_CHOICES): self.abort(400, 'Invalid state') if sort != 'created_ts': # Zap all filters in this case to reduce the number of required indexes. # Revisit according to the user requests. state = 'all' now = utils.utcnow() counts_future = self._get_counts_future(now) # This call is synchronous. try: tasks, cursor_str, sort, state = task_result.get_tasks( limit, cursor_str, sort, state, task_tags, task_name) # Prefetch the TaskRequest all at once, so that ndb's in-process cache has # it instead of fetching them one at a time indirectly when using # TaskResultSummary.request_key.get(). futures = ndb.get_multi_async(t.request_key for t in tasks) # Evaluate the counts to print the filtering columns with the associated # numbers. state_choices = self._get_state_choices(counts_future) except (search.QueryError, ValueError) as e: self.abort(400, str(e)) def safe_sum(items): return sum(items, datetime.timedelta()) def avg(items): if not items: return 0. return safe_sum(items) / len(items) def median(items): if not items: return 0. middle = len(items) / 2 if len(items) % 2: return items[middle] return (items[middle-1]+items[middle]) / 2 gen = (t.duration_now(now) for t in tasks) durations = sorted(t for t in gen if t is not None) gen = (t.pending_now(now) for t in tasks) pendings = sorted(t for t in gen if t is not None) total_cost_usd = sum(t.cost_usd for t in tasks) total_cost_saved_usd = sum( t.cost_saved_usd for t in tasks if t.cost_saved_usd) # Include the overhead in the total amount of time saved, since it's # overhead saved. # In theory, t.duration_total should always be set when t.deduped_from is # set but there has some broken entities in the datastore. total_saved = safe_sum( t.duration_total for t in tasks if t.deduped_from and t.duration_total) duration_sum = safe_sum(durations) total_saved_percent = ( (100. * total_saved.total_seconds() / duration_sum.total_seconds()) if duration_sum else 0.) params = { 'cursor': cursor_str, 'duration_average': avg(durations), 'duration_median': median(durations), 'duration_sum': duration_sum, 'has_pending': any(t.is_pending for t in tasks), 'has_running': any(t.is_running for t in tasks), 'is_admin': acl.is_admin(), 'is_privileged_user': acl.is_privileged_user(), 'limit': limit, 'now': now, 'pending_average': avg(pendings), 'pending_median': median(pendings), 'pending_sum': safe_sum(pendings), 'show_footer': bool(pendings or durations), 'sort': sort, 'sort_choices': self.SORT_CHOICES, 'state': state, 'state_choices': state_choices, 'task_name': task_name, 'task_tag': '\n'.join(task_tags), 'tasks': tasks, 'total_cost_usd': total_cost_usd, 'total_cost_saved_usd': total_cost_saved_usd, 'total_saved': total_saved, 'total_saved_percent': total_saved_percent, 'xsrf_token': self.generate_xsrf_token(), } # TODO(maruel): If admin or if the user is task's .user, show the Cancel # button. Do not show otherwise. self.response.write(template.render('swarming/user_tasks.html', params)) # Do not let dangling futures linger around. ndb.Future.wait_all(futures)
def get(self): """Handles both ndb.Query searches and search.Index().search() queries. If |task_name| is set or not affects the meaning of |cursor|. When set, the cursor is for search.Index, otherwise the cursor is for a ndb.Query. """ cursor_str = self.request.get('cursor') limit = int(self.request.get('limit', 100)) sort = self.request.get('sort', self.SORT_CHOICES[0][0]) state = self.request.get('state', self.STATE_CHOICES[0][0][0]) task_name = self.request.get('task_name', '').strip() counts = self.request.get('counts', '').strip() task_tags = [ line for line in self.request.get('task_tag', '').splitlines() if line ] if not any(sort == i[0] for i in self.SORT_CHOICES): self.abort(400, 'Invalid sort') if not any(any(state == i[0] for i in j) for j in self.STATE_CHOICES): self.abort(400, 'Invalid state') if sort != 'created_ts': # Zap all filters in this case to reduce the number of required indexes. # Revisit according to the user requests. state = 'all' now = utils.utcnow() # "Temporarily" disable the count. This is too slow on the prod server # (>10s). The fix is to have the web page do a XHR query to get the values # asynchronously. counts_future = None if counts == 'true': counts_future = self._get_counts_future(now) # This call is synchronous. try: tasks, cursor_str, sort, state = task_result.get_tasks( limit, cursor_str, sort, state, task_tags, task_name) # Prefetch the TaskRequest all at once, so that ndb's in-process cache has # it instead of fetching them one at a time indirectly when using # TaskResultSummary.request_key.get(). futures = ndb.get_multi_async(t.request_key for t in tasks) # Evaluate the counts to print the filtering columns with the associated # numbers. state_choices = self._get_state_choices(counts_future) except (search.QueryError, ValueError) as e: self.abort(400, str(e)) def safe_sum(items): return sum(items, datetime.timedelta()) def avg(items): if not items: return 0. return safe_sum(items) / len(items) def median(items): if not items: return 0. middle = len(items) / 2 if len(items) % 2: return items[middle] return (items[middle-1]+items[middle]) / 2 gen = (t.duration_now(now) for t in tasks) durations = sorted(t for t in gen if t is not None) gen = (t.pending_now(now) for t in tasks) pendings = sorted(t for t in gen if t is not None) total_cost_usd = sum(t.cost_usd for t in tasks) total_cost_saved_usd = sum( t.cost_saved_usd for t in tasks if t.cost_saved_usd) # Include the overhead in the total amount of time saved, since it's # overhead saved. # In theory, t.duration_as_seen_by_server should always be set when # t.deduped_from is set but there has some broken entities in the datastore. total_saved = safe_sum( t.duration_as_seen_by_server for t in tasks if t.deduped_from and t.duration_as_seen_by_server) duration_sum = safe_sum(durations) total_saved_percent = ( (100. * total_saved.total_seconds() / duration_sum.total_seconds()) if duration_sum else 0.) params = { 'cursor': cursor_str, 'duration_average': avg(durations), 'duration_median': median(durations), 'duration_sum': duration_sum, 'has_pending': any(t.is_pending for t in tasks), 'has_running': any(t.is_running for t in tasks), 'is_admin': acl.is_admin(), 'is_privileged_user': acl.is_privileged_user(), 'limit': limit, 'now': now, 'pending_average': avg(pendings), 'pending_median': median(pendings), 'pending_sum': safe_sum(pendings), 'show_footer': bool(pendings or durations), 'sort': sort, 'sort_choices': self.SORT_CHOICES, 'state': state, 'state_choices': state_choices, 'task_name': task_name, 'task_tag': '\n'.join(task_tags), 'tasks': tasks, 'total_cost_usd': total_cost_usd, 'total_cost_saved_usd': total_cost_saved_usd, 'total_saved': total_saved, 'total_saved_percent': total_saved_percent, 'xsrf_token': self.generate_xsrf_token(), } # TODO(maruel): If admin or if the user is task's .user, show the Cancel # button. Do not show otherwise. self.response.write(template.render('swarming/user_tasks.html', params)) # Do not let dangling futures linger around. ndb.Future.wait_all(futures)