diff options
Diffstat (limited to 'lib/srtgui/widgets.py')
-rw-r--r-- | lib/srtgui/widgets.py | 297 |
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 + |