aboutsummaryrefslogtreecommitdiffstats
path: root/lib/srtgui/widgets.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/srtgui/widgets.py')
-rw-r--r--lib/srtgui/widgets.py297
1 files changed, 281 insertions, 16 deletions
diff --git a/lib/srtgui/widgets.py b/lib/srtgui/widgets.py
index 5f5c54b1..ec2fbd42 100644
--- a/lib/srtgui/widgets.py
+++ b/lib/srtgui/widgets.py
@@ -4,7 +4,7 @@
#
# BitBake Toaster Implementation
#
-# Copyright (C) 2015 Intel Corporation
+# Copyright (C) 2023 Intel Corporation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
@@ -21,6 +21,7 @@
from django.views.generic import View, TemplateView
from django.views.decorators.cache import cache_control
+from django.utils.decorators import method_decorator
from django.shortcuts import HttpResponse
from django.core.cache import cache
from django.core.paginator import Paginator, EmptyPage
@@ -29,8 +30,15 @@ from django.template import Context, Template
from django.template import VariableDoesNotExist
from django.template import TemplateSyntaxError
from django.core.serializers.json import DjangoJSONEncoder
+from django.urls import reverse, resolve
+from django.utils import timezone
+from django.http import JsonResponse
+
+from srtgui.templatetags.jobtags import json as template_json
+from srtgui.templatetags.jobtags import sectohms
from orm.models import SrtSetting, Cve
+from orm.models import Job
import types
import json
@@ -51,6 +59,38 @@ class NoFieldOrDataName(Exception):
# quick development/debugging support
from srtgui.api import _log
+################################################
+### Helper Routines
+
+def isbalanced(s):
+ c= 0
+ ans=False
+ list1 = []
+ list2 = []
+ strcheck = ""
+ lstcheck = []
+ for i in range(len(s)):
+ if s[i] == "(":
+ strcheck = strcheck + "_" + s[i]
+ lstcheck.append(i)
+ elif s[i] == ")" and "(" in strcheck:
+ list1 = strcheck.split("_")
+ list1.pop()
+ lstcheck.pop()
+ strcheck = "_".join(list1)
+ elif s[i] == ")" and "(" not in strcheck:
+ strcheck = strcheck + "_" + s[i]
+ lstcheck.append(i)
+ list1 = strcheck.split("_")
+ list2[:0] = s
+ if len(lstcheck) > 0 :
+ for i2 in lstcheck:
+ list2.pop(lstcheck[0])
+ return "".join(list2)
+
+################################################
+### ToasterTable
+
class ToasterTable(TemplateView):
def __init__(self, *args, **kwargs):
super(ToasterTable, self).__init__()
@@ -60,7 +100,7 @@ class ToasterTable(TemplateView):
self.queryset = None
self.columns = []
- _log("ToasterTable:%s,%s" % (args,kwargs))
+# _log("ToasterTable:%s,%s" % (args,kwargs))
# map from field names to Filter instances
self.filter_map = TableFilterMap()
@@ -71,8 +111,8 @@ class ToasterTable(TemplateView):
self.default_orderby = ""
# prevent HTTP caching of table data
- @cache_control(must_revalidate=True,
- max_age=0, no_store=True, no_cache=True)
+ @method_decorator(cache_control(must_revalidate=True,
+ max_age=0, no_store=True, no_cache=True))
def dispatch(self, request, *args, **kwargs):
return super(ToasterTable, self).dispatch(request, *args, **kwargs)
@@ -96,6 +136,17 @@ class ToasterTable(TemplateView):
def get(self, request, *args, **kwargs):
if request.GET.get('format', None) == 'json':
+ # Add all URL parameters to kwargs, specifically for the
+ # case of the Toaster table JSON 'get' for the 'filter' AJAX
+ # call which does not include the request header parameters
+ tableParams = self.request.GET.get('tableParams','')
+ for param in tableParams.split(','):
+ pos = param.find('=')
+ if 0 < pos:
+ name = param[:param.find('=')]
+ value = param[param.find('=')+1:]
+ kwargs[name] = value
+
self.setup_queryset(*args, **kwargs)
# Put the project id into the context for the static_data_template
if 'pid' in kwargs:
@@ -228,25 +279,89 @@ class ToasterTable(TemplateView):
TableFilterAction* before its filter is applied and may modify the
queryset returned by the filter
"""
+
self.setup_filters(**kwargs)
try:
- filter_name, action_name = filters.split(':')
+# filter_name, action_name = filters.split(':')
+ if len(filters.split(",")) < 2 :
+ filter_name, action_name = filters.split(':')
action_params = unquote_plus(filter_value)
except ValueError:
return
- if "all" in action_name:
- return
-
- table_filter = self.filter_map.get_filter(filter_name)
- action = table_filter.get_action(action_name)
- action.set_filter_params(action_params)
- self.queryset = action.filter(self.queryset)
+# if "all" in action_name:
+# return
+
+ FilterString = ""
+ CriteriaString = ""
+ lstactionlist = []
+ if self.request.session.get('filterkey'):
+ if False:
+ if self.request.session['filterkey'].split("~")[0] != "":
+ CriteriaString = self.request.session['filterkey']
+ CriteriaString = CriteriaString.split("~")[0]
+ FilterString = str(filters) + str('|') + str(CriteriaString)
+ FilterList = FilterString.split('|')
+
+
+ # if self.request.session['filterkey'].split("~")[0] != "":
+ if len(filters.split(",")) > 1:
+ # CriteriaString = self.request.session['filterkey']
+ # CriteriaString = CriteriaString.split("~")[0]
+ # for CriteriaItem in filters.split(","):
+ # if not CriteriaItem in FilterString:
+ # FilterString = str(CriteriaItem) + str('|') + str(CriteriaString)
+ FilterList = filters.split(",")
+
+ q1 = None
+ q2 = self.queryset
+ for filterItem in FilterList:
+ #if counter == 0:
+ table_filter1 = self.filter_map.get_filter(filterItem.split(":")[0])
+ action1 = table_filter1.get_action(filterItem.split(":")[1])
+ action1.set_filter_params(action_params)
+ q1 = action1.filter(q2)
+ q2 = q1
+ lstactionlist.append(filterItem.split(":")[1])
+ self.queryset = q1
+ else:
+ table_filter = self.filter_map.get_filter(filter_name)
+ action = table_filter.get_action(action_name)
+ action.set_filter_params(action_params)
+ self.queryset = action.filter(self.queryset)
+ FilterString = str(filters)
+ lstactionlist.append(action_name)
+ else:
+ table_filter = self.filter_map.get_filter(filter_name)
+ action = table_filter.get_action(action_name)
+ action.set_filter_params(action_params)
+ self.queryset = action.filter(self.queryset)
+ FilterString = str(filters)
+ lstactionlist.append(action_name)
+
+ _log("FOO:APPLY_FILTER:FILTER:%s" % action_params)
+
+ strquerystring = self.queryset.query.__str__()
+ qstring1 = strquerystring.replace ('AND', 'AND\n')
+ qstring2 = qstring1.replace ('OR', 'OR\n')
+ tar = re.findall(r"(?<==).+(?= AND)|(?<==)(?<==).+(?= OR )|(?<==).+(?=[)])|(?<==).+(?= OR)", qstring2)
+ for item in tar:
+ if len(re.findall(r"[A-Za-z]", item.strip())) != 0 :
+ item = isbalanced(item)
+ strquerystring = strquerystring.replace(item, '"'+item.strip()+'"' )
+ self.request.session['filterkey'] = str(FilterString) + str('~') + strquerystring
def apply_orderby(self, orderby):
# Note that django will execute this when we try to retrieve the data
- self.queryset = self.queryset.order_by(orderby)
+ if False:
+ # Use parent order field if present (for column computed from existing column)
+ order_by = re.sub(r'.*__parent_', '', orderby)
+# order_by = orderby
+ self.queryset = self.queryset.order_by(order_by)
+ else:
+ self.queryset = self.queryset.order_by(orderby)
+ # self.request.session['filterkey'] = str('~') + str(self.queryset.query)
def apply_search(self, search_term):
"""Creates a query based on the model's search_allowed_fields"""
@@ -285,9 +400,19 @@ class ToasterTable(TemplateView):
else:
search_queries = queries
+ _log("FOO:APPLY_SEARCH:FILTER:%s" % search_queries)
self.queryset = self.queryset.filter(search_queries)
-
- def apply_row_customization(self, row):
+ strquerystring = self.queryset.query.__str__()
+ qstring1 = strquerystring.replace ('AND', 'AND\n')
+ qstring2 = qstring1.replace ('OR', 'OR\n')
+ tar = re.findall(r"(?<==).+(?= AND)|(?<==)(?<==).+(?= OR )|(?<==).+(?=[)])|(?<==).+(?= OR)", qstring2)
+ for item in tar:
+ if len(re.findall(r"[A-Za-z]", item.strip())) != 0 :
+ item = isbalanced(item)
+ strquerystring = strquerystring.replace(item, '"'+item.strip()+'"' )
+ self.request.session['filterkey'] = str('~') + str(strquerystring)
+
+ def apply_row_customization(self, row, **kwargs):
""" function to implement in the subclass which supports
row data customization in the respective table handler """
return row
@@ -311,6 +436,11 @@ class ToasterTable(TemplateView):
orderby = request.GET.get("orderby", None)
nocache = request.GET.get("nocache", None)
+ # Test if clear filters from session
+ if filters == "":
+ if request.session.get('filterkey'):
+ del request.session['filterkey']
+
# Make a unique cache name
cache_name = self.__class__.__name__
@@ -339,6 +469,8 @@ class ToasterTable(TemplateView):
self.setup_columns(**kwargs)
+ self.request.session['nofilterkey'] = str('~') + str(self.queryset.query)
+
if search:
self.apply_search(search)
if filters:
@@ -428,7 +560,7 @@ class ToasterTable(TemplateView):
data['rows'].append(required_data)
# apply any row data customization override before converted to JSON
- data = self.apply_row_customization(data)
+ data = self.apply_row_customization(data, **kwargs)
data = json.dumps(data, indent=2, cls=DjangoJSONEncoder)
cache.set(cache_name, data, 60*30)
@@ -491,3 +623,136 @@ class ToasterTypeAhead(View):
pass
+class MostRecentJobsView(View):
+ def _was_yesterday_or_earlier(self, completed_on):
+ now = timezone.now()
+ delta = now - completed_on
+
+ if delta.days >= 1:
+ return True
+
+ return False
+
+ def get(self, request, *args, **kwargs):
+ """
+ Returns a list of jobs in JSON format.
+ """
+
+ recent_job_objs = Job.get_recent()
+ recent_jobs = []
+
+ for job_obj in recent_job_objs:
+## cancel_url = \
+## reverse('xhr_jobrequest', args=(job_obj.sprint.pk,))
+# cancel_url = \
+# reverse('xhr_jobrequest', )
+ cancel_url = \
+ ''
+
+ job = {}
+ job['id'] = job_obj.pk
+
+ tasks_complete_percentage = 0
+ if job_obj.status in (Job.SUCCESS, Job.ERRORS):
+ tasks_complete_percentage = 100
+ elif job_obj.status == Job.INPROGRESS:
+ tasks_complete_percentage = job_obj.completeper()
+
+ job['tasks_complete_percentage'] = tasks_complete_percentage
+
+ job['state'] = job_obj.get_status_text
+
+ job['errors'] = job_obj.errors
+
+ job['warnings'] = job_obj.warnings
+
+ if job_obj.completed_on and job_obj.started_on:
+ timespent = job_obj.completed_on - job_obj.started_on
+ job['jobtime'] = sectohms(timespent.total_seconds())
+ else:
+ job['jobtime'] = 0
+
+ job['cancel_url'] = cancel_url
+
+ job['job_targets_json'] = \
+ template_json(job_obj.name)
+
+ # convert completed_on time to user's timezone
+ if job_obj.completed_on:
+ completed_on = job_obj.completed_on
+
+ completed_on_template = '%H:%M'
+ if self._was_yesterday_or_earlier(completed_on):
+ completed_on_template = '%d/%m/%Y ' + completed_on_template
+ else:
+ completed_on_template = 'Today ' + completed_on_template
+ job['completed_on'] = completed_on.strftime(
+ completed_on_template)
+ else:
+ job['completed_on'] = 'In progress...'
+
+ job['targets'] = job_obj.message #current remote command
+
+ if job_obj.refresh:
+ # Right now a binary flag, later maybe a timeout counter
+ job['refresh'] = '1' #remote page refresh request
+ job_obj.refresh = 0
+ job_obj.save()
+ else:
+ job['refresh'] = '0'
+
+ recent_jobs.append(job)
+
+ return JsonResponse(recent_jobs, safe=False)
+
+class XhrJobRequest(View):
+
+ def error_response(error):
+ return JsonResponse({"error": error})
+
+ def get(self, request, *args, **kwargs):
+ return HttpResponse()
+
+ def post(self, request, *args, **kwargs):
+ """
+ Job control
+
+ Entry point: /xhr_jobrequest/<project_id>
+ Method: POST
+
+ Args:
+ id: id of job to change
+ jobCancel = job_request_id ...
+ jobDelete = id ...
+
+ Returns:
+ {"error": "ok"}
+ or
+ {"error": <error message>}
+ """
+
+ if 'jobCancel' in request.POST:
+ for i in request.POST['jobCancel'].strip().split(" "):
+ try:
+ job = Job.objects.get(pk=i)
+ job.cancel()
+ except Job.DoesNotExist:
+ return error_response('No such job request id %s' % i)
+
+ return JsonResponse({"error": 'ok'})
+
+ if 'jobDelete' in request.POST:
+ for i in request.POST['jobDelete'].strip().split(" "):
+ try:
+ Job.objects.select_for_update().get(
+ pk=i,
+ state__lte=Job.INPROGRESS).delete()
+
+ except Job.DoesNotExist:
+ pass
+ return error_response("ok")
+
+ response = HttpResponse()
+ response.status_code = 500
+ return response
+