diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 90e52ed883..525c449bc0 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -5,6 +5,7 @@ import dateutil import functools import html +import itertools import logging import re import requests @@ -20,9 +21,10 @@ from urllib3.exceptions import ConnectTimeoutError # Django from django.conf import settings from django.core.exceptions import FieldError, ObjectDoesNotExist -from django.db.models import Q, Sum +from django.db.models import Q, Sum, Count from django.db import IntegrityError, ProgrammingError, transaction, connection from django.db.models.fields.related import ManyToManyField, ForeignKey +from django.db.models.functions import Trunc from django.shortcuts import get_object_or_404 from django.utils.safestring import mark_safe from django.utils.timezone import now @@ -47,9 +49,6 @@ from rest_framework import status from rest_framework_yaml.parsers import YAMLParser from rest_framework_yaml.renderers import YAMLRenderer -# QSStats -import qsstats - # ANSIConv import ansiconv @@ -283,30 +282,50 @@ class DashboardJobsGraphView(APIView): success_query = success_query.filter(instance_of=models.ProjectUpdate) failed_query = failed_query.filter(instance_of=models.ProjectUpdate) - success_qss = qsstats.QuerySetStats(success_query, 'finished') - failed_qss = qsstats.QuerySetStats(failed_query, 'finished') - - start_date = now() + end = now() + interval = 'day' if period == 'month': - end_date = start_date - dateutil.relativedelta.relativedelta(months=1) - interval = 'days' + start = end - dateutil.relativedelta.relativedelta(months=1) elif period == 'two_weeks': - end_date = start_date - dateutil.relativedelta.relativedelta(weeks=2) - interval = 'days' + start = end - dateutil.relativedelta.relativedelta(weeks=2) elif period == 'week': - end_date = start_date - dateutil.relativedelta.relativedelta(weeks=1) - interval = 'days' + start = end - dateutil.relativedelta.relativedelta(weeks=1) elif period == 'day': - end_date = start_date - dateutil.relativedelta.relativedelta(days=1) - interval = 'hours' + start = end - dateutil.relativedelta.relativedelta(days=1) + interval = 'hour' else: return Response({'error': _('Unknown period "%s"') % str(period)}, status=status.HTTP_400_BAD_REQUEST) dashboard_data = {"jobs": {"successful": [], "failed": []}} - for element in success_qss.time_series(end_date, start_date, interval=interval): - dashboard_data['jobs']['successful'].append([time.mktime(element[0].timetuple()), element[1]]) - for element in failed_qss.time_series(end_date, start_date, interval=interval): - dashboard_data['jobs']['failed'].append([time.mktime(element[0].timetuple()), element[1]]) + + succ_list = dashboard_data['jobs']['successful'] + fail_list = dashboard_data['jobs']['failed'] + + qs_s = ( + success_query.filter(finished__range=(start, end)) + .annotate(d=Trunc('finished', interval, tzinfo=end.tzinfo)) + .order_by() + .values('d') + .annotate(agg=Count('id', distinct=True)) + ) + data_s = {item['d']: item['agg'] for item in qs_s} + qs_f = ( + failed_query.filter(finished__range=(start, end)) + .annotate(d=Trunc('finished', interval, tzinfo=end.tzinfo)) + .order_by() + .values('d') + .annotate(agg=Count('id', distinct=True)) + ) + data_f = {item['d']: item['agg'] for item in qs_f} + + start_date = start.replace(hour=0, minute=0, second=0, microsecond=0) + for d in itertools.count(): + date = start_date + dateutil.relativedelta.relativedelta(days=d) + if date > end: + break + succ_list.append([time.mktime(date.timetuple()), data_s.get(date, 0)]) + fail_list.append([time.mktime(date.timetuple()), data_f.get(date, 0)]) + return Response(dashboard_data) diff --git a/awx/main/dispatch/pool.py b/awx/main/dispatch/pool.py index 841b587d8e..3310f06997 100644 --- a/awx/main/dispatch/pool.py +++ b/awx/main/dispatch/pool.py @@ -466,7 +466,7 @@ class AutoscalePool(WorkerPool): task_name = 'unknown' if isinstance(body, dict): task_name = body.get('task') - logger.warn(f'Workers maxed, queuing {task_name}, load: {sum(len(w.managed_tasks) for w in self.workers)} / {len(self.workers)}') + logger.warning(f'Workers maxed, queuing {task_name}, load: {sum(len(w.managed_tasks) for w in self.workers)} / {len(self.workers)}') return super(AutoscalePool, self).write(preferred_queue, body) except Exception: for conn in connections.all(): diff --git a/docs/licenses/django-qsstats-magic.txt b/docs/licenses/django-qsstats-magic.txt deleted file mode 100644 index adef47b952..0000000000 --- a/docs/licenses/django-qsstats-magic.txt +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2010, Matt Croydon, Mikhail Korobov -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 the tastypie 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 MATT CROYDON 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. diff --git a/requirements/requirements.in b/requirements/requirements.in index 00779e760c..3a125fec79 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -19,7 +19,6 @@ django-guid==3.2.1 django-oauth-toolkit==1.4.1 django-polymorphic django-pglocks -django-qsstats-magic django-redis django-solo django-split-settings diff --git a/requirements/requirements.txt b/requirements/requirements.txt index c407a3f5ee..3a50cd03a0 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -115,9 +115,6 @@ django-pglocks==1.0.4 # via -r /awx_devel/requirements/requirements.in django-polymorphic==3.1.0 # via -r /awx_devel/requirements/requirements.in -django-qsstats-magic==1.1.0 - # via -r /awx_devel/requirements/requirements.in - # via -r /awx_devel/requirements/requirements_git.txt django-redis==4.5.0 # via -r /awx_devel/requirements/requirements.in django-solo==2.0.0