Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 1 | # |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 2 | # BitBake Toaster Implementation |
| 3 | # |
| 4 | # Copyright (C) 2013 Intel Corporation |
| 5 | # |
Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 6 | # SPDX-License-Identifier: GPL-2.0-only |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 7 | # |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 8 | |
| 9 | from datetime import datetime, timedelta |
| 10 | from os.path import relpath |
| 11 | import re |
| 12 | from django import template |
| 13 | from django.utils import timezone |
| 14 | from django.template.defaultfilters import filesizeformat |
| 15 | import json as JsonLib |
| 16 | from django.utils.safestring import mark_safe |
| 17 | |
| 18 | register = template.Library() |
| 19 | |
| 20 | @register.simple_tag |
| 21 | def time_difference(start_time, end_time): |
| 22 | return end_time - start_time |
| 23 | |
| 24 | @register.filter(name = 'sectohms') |
| 25 | def 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') |
| 35 | def 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") |
| 41 | def 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.assignment_tag |
| 48 | def 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") |
| 59 | def 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 |
| 77 | def divide(value, arg): |
| 78 | if int(arg) == 0: |
| 79 | return -1 |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 80 | return int(value) // int(arg) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 81 | |
| 82 | @register.filter |
| 83 | def multiply(value, arg): |
| 84 | return int(value) * int(arg) |
| 85 | |
| 86 | @register.assignment_tag |
| 87 | def datecompute(delta, start = timezone.now()): |
| 88 | return start + timedelta(delta) |
| 89 | |
| 90 | |
| 91 | @register.filter(name = 'sortcols') |
| 92 | def sortcols(tablecols): |
| 93 | return sorted(tablecols, key = lambda t: t['name']) |
| 94 | |
| 95 | @register.filter |
| 96 | def 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: |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 102 | return 'class=text-muted' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 103 | elif task_object.outcome == task_object.OUTCOME_FAILED: |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 104 | return 'class=text-danger' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 105 | elif task_object.outcome == task_object.OUTCOME_SUCCESS and show_green: |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 106 | return 'class=text-success' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 107 | else: |
| 108 | return '' |
| 109 | |
| 110 | @register.filter |
| 111 | def 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 |
| 122 | def 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 |
| 133 | def 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 |
| 139 | def 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 |
| 151 | def filtered_packagespec(value): |
| 152 | """Strip off empty version and revision""" |
| 153 | return re.sub(r'(--$)', '', value) |
| 154 | |
| 155 | @register.filter |
| 156 | def 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 |
| 167 | def variable_parent_name(value): |
| 168 | """ filter extended variable names to the parent name |
| 169 | """ |
| 170 | value=re.sub('_\$.*', '', value) |
| 171 | return re.sub('_[a-z].*', '', value) |
| 172 | |
| 173 | @register.filter |
| 174 | def 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 |
| 193 | def 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 |
| 205 | def 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 |
| 211 | def 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 == None) else installed_size |
| 216 | |
| 217 | @register.filter |
| 218 | def 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 |
| 226 | def 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 == None or size == '': |
| 232 | size = package_object.size |
| 233 | |
| 234 | return '{:.1%}'.format(float(size)/float(total_size)) |
| 235 | |
| 236 | from django.utils.safestring import mark_safe |
| 237 | @register.filter |
| 238 | def format_vpackage_rowclass(size): |
| 239 | if size == -1: |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 240 | return mark_safe('class="text-muted"') |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 241 | return '' |
| 242 | |
| 243 | @register.filter |
| 244 | def format_vpackage_namehelp(name): |
| 245 | r = name + ' ' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 246 | r += '<span class="glyphicon glyphicon-question-sign get-help hover-help"' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 247 | r += ' title = "' + name + ' has not been built">' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 248 | r += '</span>' |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 249 | return mark_safe(r) |
| 250 | |
| 251 | @register.filter |
| 252 | def 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 |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 261 | def 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 |
| 273 | def 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 |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 279 | |
| 280 | |
| 281 | @register.filter |
| 282 | def 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) |