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