1# 2# BitBake Toaster Implementation 3# 4# Copyright (C) 2013 Intel Corporation 5# 6# SPDX-License-Identifier: GPL-2.0-only 7# 8 9from datetime import timedelta 10from os.path import relpath 11import re 12from django import template 13from django.utils import timezone 14from django.template.defaultfilters import filesizeformat 15import json as JsonLib 16from django.utils.safestring import mark_safe 17 18register = template.Library() 19 20@register.simple_tag 21def time_difference(start_time, end_time): 22 return end_time - start_time 23 24@register.filter(name = 'sectohms') 25def sectohms(time): 26 try: 27 tdsec = int(time) 28 except ValueError: 29 tdsec = 0 30 hours = int(tdsec / 3600) 31 return "%02d:%02d:%02d" % (hours, int((tdsec - (hours * 3600))/ 60), int(tdsec) % 60) 32 33 34@register.filter(name = 'get_tasks') 35def get_tasks(queryset): 36 return list(target + ':' + task if task else target \ 37 for target, task in queryset.values_list('target', 'task')) 38 39 40@register.filter(name = "json") 41def json(value, default = None): 42 # JSON spec says that "\/" is functionally identical to "/" to allow for HTML-tag embedding in JSON strings 43 # unfortunately, I can't find any option in the json module to turn on forward-slash escaping, so we do 44 # it manually here 45 return mark_safe(JsonLib.dumps(value, indent=2, default = default, ensure_ascii=False).replace('</', '<\\/')) 46 47@register.simple_tag 48def query(qs, **kwargs): 49 """ template tag which allows queryset filtering. Usage: 50 {% query books author=author as mybooks %} 51 {% for book in mybooks %} 52 ... 53 {% endfor %} 54 """ 55 return qs.filter(**kwargs) 56 57 58@register.filter("whitespace_slice") 59def whitespace_space_filter(value, arg): 60 try: 61 bits = [] 62 for x in arg.split(":"): 63 if len(x) == 0: 64 bits.append(None) 65 else: 66 # convert numeric value to the first whitespace after 67 first_whitespace = value.find(" ", int(x)) 68 if first_whitespace == -1: 69 bits.append(int(x)) 70 else: 71 bits.append(first_whitespace) 72 return value[slice(*bits)] 73 except (ValueError, TypeError): 74 raise 75 76@register.filter 77def divide(value, arg): 78 if int(arg) == 0: 79 return -1 80 return int(value) // int(arg) 81 82@register.filter 83def multiply(value, arg): 84 return int(value) * int(arg) 85 86@register.simple_tag 87def datecompute(delta, start = timezone.now()): 88 return start + timedelta(delta) 89 90 91@register.filter(name = 'sortcols') 92def sortcols(tablecols): 93 return sorted(tablecols, key = lambda t: t['name']) 94 95@register.filter 96def task_color(task_object, show_green=False): 97 """ Return css class depending on Task execution status and execution outcome. 98 By default, green is not returned for executed and successful tasks; 99 show_green argument should be True to get green color. 100 """ 101 if not task_object.task_executed: 102 return 'class=text-muted' 103 elif task_object.outcome == task_object.OUTCOME_FAILED: 104 return 'class=text-danger' 105 elif task_object.outcome == task_object.OUTCOME_SUCCESS and show_green: 106 return 'class=text-success' 107 else: 108 return '' 109 110@register.filter 111def filtered_icon(options, filter): 112 """Returns btn-primary if the filter matches one of the filter options 113 """ 114 for option in options: 115 if filter == option[1]: 116 return "btn-primary" 117 if ('daterange' == option[1]) and filter.startswith(option[4]): 118 return "btn-primary" 119 return "" 120 121@register.filter 122def filtered_tooltip(options, filter): 123 """Returns tooltip for the filter icon if the filter matches one of the filter options 124 """ 125 for option in options: 126 if filter == option[1]: 127 return "Showing only %s"%option[0] 128 if ('daterange' == option[1]) and filter.startswith(option[4]): 129 return "Showing only %s"%option[0] 130 return "" 131 132@register.filter 133def format_none_and_zero(value): 134 """Return empty string if the value is None, zero or Not Applicable 135 """ 136 return "" if (not value) or (value == 0) or (value == "0") or (value == 'Not Applicable') else value 137 138@register.filter 139def filtered_filesizeformat(value): 140 """ 141 If the value is -1 return an empty string. Otherwise, 142 change output from fileformatsize to suppress trailing '.0' 143 and change 'bytes' to 'B'. 144 """ 145 if value == -1: 146 return '' 147 148 return filesizeformat(value).replace("bytes", "B") 149 150@register.filter 151def filtered_packagespec(value): 152 """Strip off empty version and revision""" 153 return re.sub(r'(--$)', '', value) 154 155@register.filter 156def check_filter_status(options, filter): 157 """Check if the active filter is among the available options, and return 'checked' 158 if filter is not active. 159 Used in FilterDialog to select the first radio button if the filter is not active. 160 """ 161 for option in options: 162 if filter == option[1]: 163 return "" 164 return "checked" 165 166@register.filter 167def variable_parent_name(value): 168 """ filter extended variable names to the parent name 169 """ 170 value = re.sub(r'_\$.*', '', value) 171 return re.sub(r'_[a-z].*', '', value) 172 173@register.filter 174def filter_setin_files(file_list, matchstr): 175 """Filter/search the 'set in' file lists.""" 176 result = [] 177 search, filter = matchstr.split(':') 178 for pattern in (search, filter): 179 if pattern: 180 for fobj in file_list: 181 fname = fobj.file_name 182 if fname not in result and re.search(pattern, fname): 183 result.append(fname) 184 185 # no filter, show last file (if any) 186 last = list(file_list)[-1].file_name 187 if not filter and last not in result: 188 result.append(last) 189 190 return result 191 192@register.filter 193def string_slice(strvar,slicevar): 194 """ slice a string with |string_slice:'[first]:[last]' 195 """ 196 first,last= slicevar.partition(':')[::2] 197 if first=='': 198 return strvar[:int(last)] 199 elif last=='': 200 return strvar[int(first):] 201 else: 202 return strvar[int(first):int(last)] 203 204@register.filter 205def string_remove_regex(value,ex): 206 """ remove sub-string of string that matches regex 207 """ 208 return re.sub(ex, '', value) 209 210@register.filter 211def filtered_installedsize(size, installed_size): 212 """If package.installed_size not null and not empty return it, 213 else return package.size 214 """ 215 return size if (installed_size == 0) or (installed_size == "") or (installed_size is None) else installed_size 216 217@register.filter 218def filtered_packageversion(version, revision): 219 """ Emit "version-revision" if version and revision are not null 220 else "version" if version is not null 221 else "" 222 """ 223 return "" if (not version or version == "") else version if (not revision or revision == "") else version + "-" + revision 224 225@register.filter 226def filter_sizeovertotal(package_object, total_size): 227 """ Return the % size of the package over the total size argument 228 formatted nicely. 229 """ 230 size = package_object.installed_size 231 if size is None or size == '': 232 size = package_object.size 233 234 return '{:.1%}'.format(float(size)/float(total_size)) 235 236from django.utils.safestring import mark_safe 237@register.filter 238def format_vpackage_rowclass(size): 239 if size == -1: 240 return mark_safe('class="text-muted"') 241 return '' 242 243@register.filter 244def format_vpackage_namehelp(name): 245 r = name + ' ' 246 r += '<span class="glyphicon glyphicon-question-sign get-help hover-help"' 247 r += ' title = "' + name + ' has not been built">' 248 r += '</span>' 249 return mark_safe(r) 250 251@register.filter 252def get_dict_value(dictionary, key): 253 """ return the value of a dictionary key 254 """ 255 try: 256 return dictionary[key] 257 except (KeyError, IndexError): 258 return '' 259 260@register.filter 261def is_shaid(text): 262 """ return True if text length is 40 characters and all hex-digits 263 """ 264 try: 265 int(text, 16) 266 if len(text) == 40: 267 return True 268 return False 269 except ValueError: 270 return False 271 272@register.filter 273def cut_path_prefix(fullpath, prefixes): 274 """Cut path prefix from fullpath.""" 275 for prefix in prefixes: 276 if fullpath.startswith(prefix): 277 return relpath(fullpath, prefix) 278 return fullpath 279 280 281@register.filter 282def for_target(package_dependencies, target): 283 """ filter the dependencies to be displayed by the supplied target 284 if no dependences are found for the target then return the predicted 285 dependences""" 286 return package_dependencies.for_target_or_none(target) 287