From b128d0675ac5c5a0752f6d335f9804bc3072c039 Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 07:58:52 +0000 Subject: [PATCH 1/10] fix issue 222: Don't hide results in kojiweb The same with patch1 except add two paramerters hence we can ajust abbr result lines and length from taskinfo page --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 0ac8902..c3a2d07 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -554,10 +554,15 @@ def tasks(environ, owner=None, state='active', view='tree', method='all', hostID return _genHTML(environ, 'tasks.chtml') -def taskinfo(environ, taskID): +def taskinfo(environ, taskID, maxAbbrResultLines=None, maxAbbrResultLen=None): server = _getServer(environ) values = _initValues(environ, 'Task Info', 'tasks') + if maxAbbrResultLines is not None: + maxAbbrResultLines = int(maxAbbrResultLines) + if maxAbbrResultLen is not None: + maxAbbrResultLen = int(maxAbbrResultLen) + taskID = int(taskID) task = server.getTaskInfo(taskID, request=True) if not task: @@ -670,6 +675,11 @@ def taskinfo(environ, taskID): values['result'] = None values['excClass'] = None + full_result_text, abbr_result_text = kojiweb.util.task_result_to_html_or_string( + values['result'], values['excClass'], maxAbbrResultLines, maxAbbrResultLen) + values['full_result_text'] = full_result_text + values['abbr_result_text'] = abbr_result_text + output = server.listTaskOutput(task['id']) output.sort(_sortByExtAndName) values['output'] = output diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index 21aa7eb..11e4572 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -393,22 +393,19 @@ $value Result - -
- #if $excClass -
-          #if $hasattr($result, 'faultString')
-$cgi.escape($result.faultString.strip())
-          #else
-${excClass.__name__}: $cgi.escape($str($result))
-          #end if
-          
- #elif $isinstance($result, dict) - $printMap($result) + #if $abbr_result_text + +
+ $abbr_result_text +
+
+ $full_result_text +
#else - $printValue('', $result) - #end if +
+ $full_result_text
+ #end if @@ -430,25 +427,29 @@ ${excClass.__name__}: $cgi.escape($str($result)) +#if $abbr_result_text - +#end if #include "includes/footer.chtml" diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 5aa1cb3..340a8cf 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -31,9 +31,8 @@ from socket import error as socket_error from socket import sslerror as socket_sslerror from xmlrpclib import ProtocolError from xml.parsers.expat import ExpatError - -class NoSuchException(Exception): - pass +from HTMLParser import HTMLParser +import cgi try: # pyOpenSSL might not be around @@ -597,3 +596,91 @@ a network issue or load issues on the server.""" else: str = "An error has occurred while processing your request." return str, level + +def _parse_value(key, value, sep=', '): + _str = None + _len = None + htmlparser = HTMLParser() + if key in ('brootid', 'buildroot_id'): + value = str(value) + _str = """%s""" % (value, value) + _len = len(htmlparser.unescape(_str)) + elif isinstance(value, list): + _str = sep.join([str(val) for val in value]) + elif isinstance(value, dict): + _str = sep.join(['%s=%s' % ((n == '' and "''" or n), v) for n, v in value.items()]) + else: + _str = str(value) + if _str is None: + _str = '' + if _len is None: + _len = len(_str) + return _len, _str + +def task_result_to_html_or_string(result=None, exc_class=None, + max_abbr_lines=None, max_abbr_len=None): + """convert the result to a HTML fragment or a string. + + Returns a tuple: (full_html_or_string, abbreviated_html_or_string) + """ + default_max_abbr_result_lines = 11 + default_max_abbr_result_len = 512 + if max_abbr_lines is None: + max_abbr_lines = default_max_abbr_result_lines + if max_abbr_len is None: + max_abbr_len = default_max_abbr_result_len + full_ret_str = '' + abbr_ret_str = '' + lines = [] + _parse_properties = lambda props:', '.join( + [v is not None and '%s=%s' % (n, v) or str(n) for n, v in props.items()]) + if exc_class: + if hasattr(result, 'faultString'): + _str = cgi.escape(result.faultString.strip()) + else: + _str = "%s: %s" % (exc_class.__name__, cgi.escape(str(result))) + lines.append({"text": _str, "length": len(_str), "begin_tag": "
", "end_tag": "
"}) + elif isinstance(result, dict): + htmlparser = HTMLParser() + for k, v in result.items(): + if k == 'properties': + _str = "properties = %s" % _parse_properties(v) + elif k != '__starstar': + _str = "%s = %s" % (k, _parse_value(k, v)[1]) + _len = len(htmlparser.unescape(_str)) + lines.append({"text": _str, "length": _len, "begin_tag": "", "end_tag": "
"}) + else: + if result is not None: + _len, _str = _parse_value('', result) + lines.append({"text": _str, "length": _len, "begin_tag": "", "end_tag": ""}) + if not lines: + return full_ret_str, abbr_ret_str + + total_lines = len(lines) + full_result_len = reduce(lambda s, l: s + l, [line["length"] for line in lines]) + total_abbr_lines = 0 + total_abbr_len = 0 + for line in lines: + line_len = line["length"] + line_text = line["text"] + begin_tag = line["begin_tag"] + end_tag = line["end_tag"] + full_ret_str += "%s%s%s" % (begin_tag, line_text, end_tag) + if total_lines < max_abbr_lines and full_result_len < max_abbr_len: + continue + + if total_abbr_lines >= max_abbr_lines: + break + else: + total_abbr_lines += 1 + if total_abbr_len >= max_abbr_len: + break + if total_abbr_len + line_len >= max_abbr_len: + left_abbr_len = max_abbr_len - total_abbr_len + total_abbr_len = max_abbr_len + line_text = "%s ... ..." % line_text[:left_abbr_len] + else: + total_abbr_len += line_len + abbr_ret_str += "%s%s%s" % (begin_tag, line_text, end_tag) + + return full_ret_str, abbr_ret_str From 27106c86e75f90b8ef66441c393f25465a5223fa Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 07:59:13 +0000 Subject: [PATCH 2/10] Fix as mikem review result. replace reduce func by sum func max lines is honored for dictionary results follow PEP8 style do not require HTMLParser from url args remove maxAbbrResultLines/maxAbbrResultLen --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index c3a2d07..fe2b105 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -554,15 +554,10 @@ def tasks(environ, owner=None, state='active', view='tree', method='all', hostID return _genHTML(environ, 'tasks.chtml') -def taskinfo(environ, taskID, maxAbbrResultLines=None, maxAbbrResultLen=None): +def taskinfo(environ, taskID): server = _getServer(environ) values = _initValues(environ, 'Task Info', 'tasks') - if maxAbbrResultLines is not None: - maxAbbrResultLines = int(maxAbbrResultLines) - if maxAbbrResultLen is not None: - maxAbbrResultLen = int(maxAbbrResultLen) - taskID = int(taskID) task = server.getTaskInfo(taskID, request=True) if not task: @@ -676,7 +671,7 @@ def taskinfo(environ, taskID, maxAbbrResultLines=None, maxAbbrResultLen=None): values['excClass'] = None full_result_text, abbr_result_text = kojiweb.util.task_result_to_html_or_string( - values['result'], values['excClass'], maxAbbrResultLines, maxAbbrResultLen) + values['result'], values['excClass']) values['full_result_text'] = full_result_text values['abbr_result_text'] = abbr_result_text diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 340a8cf..df8950a 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -31,7 +31,6 @@ from socket import error as socket_error from socket import sslerror as socket_sslerror from xmlrpclib import ProtocolError from xml.parsers.expat import ExpatError -from HTMLParser import HTMLParser import cgi try: @@ -597,14 +596,114 @@ a network issue or load issues on the server.""" str = "An error has occurred while processing your request." return str, level + +class TaskResultFragment(object): + """This class permits us to compose HTML fragment by the default + composer method or self-defined composer function. + + The composer function should have the following prototype: + + composer(TaskResultFragment(), length=None) + + return a string that is an available HTML fragment. + """ + def __init__(self, text='', size=None, need_escape=None, escaped=None, + begin_tag='', end_tag='', composer=None): + self.text = text + if size is None: + self.size = len(text) + else: + self.size = size + self.need_escape = need_escape + self.escaped = escaped + self.begin_tag = begin_tag + self.end_tag = end_tag + if composer is None: + self.composer = self.default_composer + else: + self.composer = lambda length=None: composer(self, length) + + def default_composer(self, length=None): + import cgi + if length is None: + text = self.text + else: + text = self.text[:length] + if self.need_escape and not self.escaped: + text = cgi.escape(text) + if self.size > 0 and text == '': + text = '...' + return '%s%s%s' % (self.begin_tag, text, self.end_tag) + + +class TaskResultLine(object): + """This class permits us from several TaskResultFragment instances + to compose an HTML fragment that ends with a line break. You + can use the default composer method or give a self-defined version. + + The composer function prototype should be + + composer(TaskResultLine(), length=None) + + return a string that is an available HTML fragment. + """ + def __init__(self, fragments=None, need_escape=None, escaped=True, + begin_tag='', end_tag='
', composer=None): + if fragments is None: + self.fragments = [] + else: + self.fragments = fragments + + self.need_escape = need_escape + self.escaped = escaped + self.begin_tag = begin_tag + self.end_tag = end_tag + if composer is None: + self.composer = self.default_composer + else: + self.composer = lambda length=None: composer(self, length) + self.size=self._size() + + def default_composer(self, length=None): + import cgi + line_text = '' + size = 0 + postfix = self.postfix(length) + + for fragment in self.fragments: + if length is None: + line_text += fragment.composer() + else: + if size >= length: break + remainder_size = length - size + line_text += fragment.composer(remainder_size) + size += fragment.size + + if self.need_escape and not self.escaped: + line_text = cgi.escape(line_text) + + return '%s%s%s%s' % (self.begin_tag, line_text, postfix, self.end_tag) + + def _size(self): + return sum([fragment.size for fragment in self.fragments]) + + def postfix(self, length=None): + if length is None or length >= self.size: + postfix = '' + else: + postfix = ' ... ...' + return postfix + + def _parse_value(key, value, sep=', '): _str = None - _len = None - htmlparser = HTMLParser() + begin_tag = '' + end_tag = '' + if key in ('brootid', 'buildroot_id'): - value = str(value) - _str = """%s""" % (value, value) - _len = len(htmlparser.unescape(_str)) + _str = str(value) + begin_tag = '' % _str + end_tag = '' elif isinstance(value, list): _str = sep.join([str(val) for val in value]) elif isinstance(value, dict): @@ -613,9 +712,8 @@ def _parse_value(key, value, sep=', '): _str = str(value) if _str is None: _str = '' - if _len is None: - _len = len(_str) - return _len, _str + + return TaskResultFragment(text=_str, begin_tag=begin_tag, end_tag=end_tag) def task_result_to_html_or_string(result=None, exc_class=None, max_abbr_lines=None, max_abbr_len=None): @@ -627,60 +725,83 @@ def task_result_to_html_or_string(result=None, exc_class=None, default_max_abbr_result_len = 512 if max_abbr_lines is None: max_abbr_lines = default_max_abbr_result_lines + if isinstance(result, dict): + max_abbr_lines = len(result) + 1 if max_abbr_len is None: max_abbr_len = default_max_abbr_result_len full_ret_str = '' abbr_ret_str = '' lines = [] - _parse_properties = lambda props:', '.join( - [v is not None and '%s=%s' % (n, v) or str(n) for n, v in props.items()]) + + def _parse_properties(props): + return ', '.join([v is not None and '%s=%s' % (n, v) or str(n) + for n, v in props.items()]) + if exc_class: if hasattr(result, 'faultString'): - _str = cgi.escape(result.faultString.strip()) + _str = result.faultString.strip() else: - _str = "%s: %s" % (exc_class.__name__, cgi.escape(str(result))) - lines.append({"text": _str, "length": len(_str), "begin_tag": "
", "end_tag": "
"}) + _str = "%s: %s" % (exc_class.__name__, str(result)) + fragment = TaskResultFragment(text=_str) + line = TaskResultLine( + fragments=[fragment], need_escape=True, + escaped=False, begin_tag='
', end_tag='
') + lines.append(line) elif isinstance(result, dict): - htmlparser = HTMLParser() + def composer(line, length=None): + key_fragment = line.fragments[0] + val_fragment = line.fragments[1] + if length is None: + return '%s%s = %s%s' % (line.begin_tag, key_fragment.composer(), + val_fragment.composer(), line.end_tag) + postfix = line.postfix(length) + first_part_len = len('%s = ' % key_fragment.composer()) + remainder_len = length - first_part_len + if remainder_len < 0: remainder_len = 0 + return '%s%s = %s%s%s' % ( + line.begin_tag, key_fragment.composer(), + val_fragment.composer(remainder_len), postfix, line.end_tag) + for k, v in result.items(): if k == 'properties': - _str = "properties = %s" % _parse_properties(v) + _str = "properties = %s" % _parse_properties(v) + fragment = TaskResultFragment(text=_str) + line = TaskResultLine(fragments=[fragment], need_escape=False) elif k != '__starstar': - _str = "%s = %s" % (k, _parse_value(k, v)[1]) - _len = len(htmlparser.unescape(_str)) - lines.append({"text": _str, "length": _len, "begin_tag": "", "end_tag": "
"}) + val_fragment = _parse_value(k, v) + key_fragment = TaskResultFragment(text=k) + line = TaskResultLine(fragments=[key_fragment, val_fragment], + need_escape=False, composer=composer) + lines.append(line) else: if result is not None: - _len, _str = _parse_value('', result) - lines.append({"text": _str, "length": _len, "begin_tag": "", "end_tag": ""}) + fragment = _parse_value('', result) + line = TaskResultLine(fragments=[fragment]) + lines.append(line) + if not lines: return full_ret_str, abbr_ret_str total_lines = len(lines) - full_result_len = reduce(lambda s, l: s + l, [line["length"] for line in lines]) + full_result_len = sum([line.size for line in lines]) total_abbr_lines = 0 total_abbr_len = 0 + for line in lines: - line_len = line["length"] - line_text = line["text"] - begin_tag = line["begin_tag"] - end_tag = line["end_tag"] - full_ret_str += "%s%s%s" % (begin_tag, line_text, end_tag) + line_len = line.size + full_ret_str += line.composer() + if total_lines < max_abbr_lines and full_result_len < max_abbr_len: continue + if total_abbr_lines >= max_abbr_lines or total_abbr_len >= max_abbr_len: + continue - if total_abbr_lines >= max_abbr_lines: - break - else: - total_abbr_lines += 1 - if total_abbr_len >= max_abbr_len: - break if total_abbr_len + line_len >= max_abbr_len: - left_abbr_len = max_abbr_len - total_abbr_len - total_abbr_len = max_abbr_len - line_text = "%s ... ..." % line_text[:left_abbr_len] + remainder_abbr_len = max_abbr_len - total_abbr_len else: - total_abbr_len += line_len - abbr_ret_str += "%s%s%s" % (begin_tag, line_text, end_tag) + remainder_abbr_len = None + total_abbr_lines += 1 + total_abbr_len += line_len + abbr_ret_str += line.composer(remainder_abbr_len) return full_ret_str, abbr_ret_str From 34febd31f84e3790adfc1f3aafe8dade441936ab Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 07:59:35 +0000 Subject: [PATCH 3/10] add docstring and replace func name by a good one. 1. follow PEP-257 add docstring. 2. change func naem "task_result_to_html_or_string" to "task_result_to_html". 3. add postfix instance attribute for TaskResultLine. 4. add empty_str_placeholder instance attribute for TaskResultFragment. 5. recover class NoSuchException. 6. honoring line limit for dictionaries. --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index fe2b105..30422ae 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -670,7 +670,7 @@ def taskinfo(environ, taskID): values['result'] = None values['excClass'] = None - full_result_text, abbr_result_text = kojiweb.util.task_result_to_html_or_string( + full_result_text, abbr_result_text = kojiweb.util.task_result_to_html( values['result'], values['excClass']) values['full_result_text'] = full_result_text values['abbr_result_text'] = abbr_result_text diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index df8950a..41d1deb 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -33,6 +33,9 @@ from xmlrpclib import ProtocolError from xml.parsers.expat import ExpatError import cgi +class NoSuchException(Exception): + pass + try: # pyOpenSSL might not be around from OpenSSL.SSL import Error as SSL_Error @@ -598,17 +601,24 @@ a network issue or load issues on the server.""" class TaskResultFragment(object): - """This class permits us to compose HTML fragment by the default + """Represent an HTML fragment composed from texts and tags. + + This class permits us to compose HTML fragment by the default composer method or self-defined composer function. - The composer function should have the following prototype: - - composer(TaskResultFragment(), length=None) - - return a string that is an available HTML fragment. + Public attributes: + - text + - size + - need_escape + - escaped + - begin_tag + - eng_tag + - composer + - empty_str_placeholder """ def __init__(self, text='', size=None, need_escape=None, escaped=None, - begin_tag='', end_tag='', composer=None): + begin_tag='', end_tag='', composer=None, + empty_str_placeholder=None): self.text = text if size is None: self.size = len(text) @@ -622,6 +632,10 @@ class TaskResultFragment(object): self.composer = self.default_composer else: self.composer = lambda length=None: composer(self, length) + if empty_str_placeholder is None: + self.empty_str_placeholder = '...' + else: + self.empty_str_placeholder = empty_str_placeholder def default_composer(self, length=None): import cgi @@ -632,23 +646,29 @@ class TaskResultFragment(object): if self.need_escape and not self.escaped: text = cgi.escape(text) if self.size > 0 and text == '': - text = '...' + text = self.empty_str_placeholder return '%s%s%s' % (self.begin_tag, text, self.end_tag) class TaskResultLine(object): - """This class permits us from several TaskResultFragment instances + """Represent an HTML line fragment. + + This class permits us from several TaskResultFragment instances to compose an HTML fragment that ends with a line break. You can use the default composer method or give a self-defined version. - The composer function prototype should be - - composer(TaskResultLine(), length=None) - - return a string that is an available HTML fragment. + Public attributes: + - fragments + - need_escape + - escaped + - begin_tag + - end_tag + - composer + - postfix """ def __init__(self, fragments=None, need_escape=None, escaped=True, - begin_tag='', end_tag='
', composer=None): + begin_tag='', end_tag='
', composer=None, + postfix=None): if fragments is None: self.fragments = [] else: @@ -663,6 +683,10 @@ class TaskResultLine(object): else: self.composer = lambda length=None: composer(self, length) self.size=self._size() + if postfix is None: + self._postfix = ' ... ' + else: + self._postfix = postfix def default_composer(self, length=None): import cgi @@ -689,10 +713,9 @@ class TaskResultLine(object): def postfix(self, length=None): if length is None or length >= self.size: - postfix = '' + return '' else: - postfix = ' ... ...' - return postfix + return self._postfix def _parse_value(key, value, sep=', '): @@ -715,18 +738,23 @@ def _parse_value(key, value, sep=', '): return TaskResultFragment(text=_str, begin_tag=begin_tag, end_tag=end_tag) -def task_result_to_html_or_string(result=None, exc_class=None, - max_abbr_lines=None, max_abbr_len=None): - """convert the result to a HTML fragment or a string. +def task_result_to_html(result=None, exc_class=None, + max_abbr_lines=None, max_abbr_len=None): + """convert the result to a mutiple lines HTML fragment + + Args: + result: task result. Default is empty string. + exc_class: Exception raised when access the task result. + max_abbr_lines: maximum abbreviated result lines. Default is 11. + max_abbr_len: maximum abbreviated result length. Default is 512. - Returns a tuple: (full_html_or_string, abbreviated_html_or_string) + Returns: + Tuple of full result and abbreviated result. """ default_max_abbr_result_lines = 11 default_max_abbr_result_len = 512 if max_abbr_lines is None: max_abbr_lines = default_max_abbr_result_lines - if isinstance(result, dict): - max_abbr_lines = len(result) + 1 if max_abbr_len is None: max_abbr_len = default_max_abbr_result_len full_ret_str = '' From 86a34bd01933fe76ddc4f029ac2ffb01e5bbb66f Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 07:59:50 +0000 Subject: [PATCH 4/10] fix hence the abbreviated result always with the postscript --- diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 41d1deb..e9d4a06 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -664,11 +664,9 @@ class TaskResultLine(object): - begin_tag - end_tag - composer - - postfix """ def __init__(self, fragments=None, need_escape=None, escaped=True, - begin_tag='', end_tag='
', composer=None, - postfix=None): + begin_tag='', end_tag='
', composer=None): if fragments is None: self.fragments = [] else: @@ -683,16 +681,11 @@ class TaskResultLine(object): else: self.composer = lambda length=None: composer(self, length) self.size=self._size() - if postfix is None: - self._postfix = ' ... ' - else: - self._postfix = postfix def default_composer(self, length=None): import cgi line_text = '' size = 0 - postfix = self.postfix(length) for fragment in self.fragments: if length is None: @@ -706,17 +699,11 @@ class TaskResultLine(object): if self.need_escape and not self.escaped: line_text = cgi.escape(line_text) - return '%s%s%s%s' % (self.begin_tag, line_text, postfix, self.end_tag) + return '%s%s%s' % (self.begin_tag, line_text, self.end_tag) def _size(self): return sum([fragment.size for fragment in self.fragments]) - def postfix(self, length=None): - if length is None or length >= self.size: - return '' - else: - return self._postfix - def _parse_value(key, value, sep=', '): _str = None @@ -739,7 +726,8 @@ def _parse_value(key, value, sep=', '): return TaskResultFragment(text=_str, begin_tag=begin_tag, end_tag=end_tag) def task_result_to_html(result=None, exc_class=None, - max_abbr_lines=None, max_abbr_len=None): + max_abbr_lines=None, max_abbr_len=None, + abbr_postscript=None): """convert the result to a mutiple lines HTML fragment Args: @@ -757,6 +745,8 @@ def task_result_to_html(result=None, exc_class=None, max_abbr_lines = default_max_abbr_result_lines if max_abbr_len is None: max_abbr_len = default_max_abbr_result_len + if abbr_postscript is None: + abbr_postscript = '...' full_ret_str = '' abbr_ret_str = '' lines = [] @@ -776,19 +766,19 @@ def task_result_to_html(result=None, exc_class=None, escaped=False, begin_tag='
', end_tag='
') lines.append(line) elif isinstance(result, dict): + def composer(line, length=None): key_fragment = line.fragments[0] val_fragment = line.fragments[1] if length is None: return '%s%s = %s%s' % (line.begin_tag, key_fragment.composer(), val_fragment.composer(), line.end_tag) - postfix = line.postfix(length) first_part_len = len('%s = ' % key_fragment.composer()) remainder_len = length - first_part_len if remainder_len < 0: remainder_len = 0 - return '%s%s = %s%s%s' % ( + return '%s%s = %s%s' % ( line.begin_tag, key_fragment.composer(), - val_fragment.composer(remainder_len), postfix, line.end_tag) + val_fragment.composer(remainder_len), line.end_tag) for k, v in result.items(): if k == 'properties': @@ -832,4 +822,4 @@ def task_result_to_html(result=None, exc_class=None, total_abbr_len += line_len abbr_ret_str += line.composer(remainder_abbr_len) - return full_ret_str, abbr_ret_str + return full_ret_str, "%s %s" % (abbr_ret_str, abbr_postscript) From f8714440fce26c75d9afbbbc268ebf33b0f8b01c Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 08:00:07 +0000 Subject: [PATCH 5/10] remove the escaped instance variable and let TaskResultLine to add postscript --- diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index e9d4a06..6ad08bb 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -610,22 +610,19 @@ class TaskResultFragment(object): - text - size - need_escape - - escaped - begin_tag - eng_tag - composer - empty_str_placeholder """ - def __init__(self, text='', size=None, need_escape=None, escaped=None, - begin_tag='', end_tag='', composer=None, - empty_str_placeholder=None): + def __init__(self, text='', size=None, need_escape=None, begin_tag='', + end_tag='', composer=None, empty_str_placeholder=None): self.text = text if size is None: self.size = len(text) else: self.size = size self.need_escape = need_escape - self.escaped = escaped self.begin_tag = begin_tag self.end_tag = end_tag if composer is None: @@ -643,7 +640,7 @@ class TaskResultFragment(object): text = self.text else: text = self.text[:length] - if self.need_escape and not self.escaped: + if self.need_escape: text = cgi.escape(text) if self.size > 0 and text == '': text = self.empty_str_placeholder @@ -660,32 +657,36 @@ class TaskResultLine(object): Public attributes: - fragments - need_escape - - escaped - begin_tag - end_tag - composer """ - def __init__(self, fragments=None, need_escape=None, escaped=True, - begin_tag='', end_tag='
', composer=None): + def __init__(self, fragments=None, need_escape=None, begin_tag='', + end_tag='
', composer=None): if fragments is None: self.fragments = [] else: self.fragments = fragments self.need_escape = need_escape - self.escaped = escaped self.begin_tag = begin_tag self.end_tag = end_tag if composer is None: self.composer = self.default_composer else: - self.composer = lambda length=None: composer(self, length) + + def composer_wrapper(length=None, postscript=None): + return composer(self, length, postscript) + + self.composer = composer_wrapper self.size=self._size() - def default_composer(self, length=None): + def default_composer(self, length=None, postscript=None): import cgi line_text = '' size = 0 + if postscript is None: + postscript = '' for fragment in self.fragments: if length is None: @@ -696,10 +697,10 @@ class TaskResultLine(object): line_text += fragment.composer(remainder_size) size += fragment.size - if self.need_escape and not self.escaped: + if self.need_escape: line_text = cgi.escape(line_text) - return '%s%s%s' % (self.begin_tag, line_text, self.end_tag) + return '%s%s%s%s' % (self.begin_tag, line_text, postscript, self.end_tag) def _size(self): return sum([fragment.size for fragment in self.fragments]) @@ -709,21 +710,24 @@ def _parse_value(key, value, sep=', '): _str = None begin_tag = '' end_tag = '' - + need_escape = True if key in ('brootid', 'buildroot_id'): _str = str(value) begin_tag = '' % _str end_tag = '' + need_escape = False elif isinstance(value, list): _str = sep.join([str(val) for val in value]) elif isinstance(value, dict): - _str = sep.join(['%s=%s' % ((n == '' and "''" or n), v) for n, v in value.items()]) + _str = sep.join(['%s=%s' % ((n == '' and "''" or n), v) + for n, v in value.items()]) else: _str = str(value) if _str is None: _str = '' - return TaskResultFragment(text=_str, begin_tag=begin_tag, end_tag=end_tag) + return TaskResultFragment(text=_str, need_escape=need_escape, + begin_tag=begin_tag, end_tag=end_tag) def task_result_to_html(result=None, exc_class=None, max_abbr_lines=None, max_abbr_len=None, @@ -746,7 +750,10 @@ def task_result_to_html(result=None, exc_class=None, if max_abbr_len is None: max_abbr_len = default_max_abbr_result_len if abbr_postscript is None: - abbr_postscript = '...' + abbr_postscript = ' ...' + elif not abbr_postscript.startswith(' '): + abbr_postscript.startswith = ' %s' % abbr_postscript.startswith + full_ret_str = '' abbr_ret_str = '' lines = [] @@ -760,34 +767,37 @@ def task_result_to_html(result=None, exc_class=None, _str = result.faultString.strip() else: _str = "%s: %s" % (exc_class.__name__, str(result)) - fragment = TaskResultFragment(text=_str) - line = TaskResultLine( - fragments=[fragment], need_escape=True, - escaped=False, begin_tag='
', end_tag='
') + fragment = TaskResultFragment(text=_str, need_escape=True) + line = TaskResultLine(fragments=[fragment], + begin_tag='
', end_tag='
') lines.append(line) elif isinstance(result, dict): - def composer(line, length=None): + def composer(line, length=None, postscript=None): + if postscript is None: + postscript = '' key_fragment = line.fragments[0] val_fragment = line.fragments[1] if length is None: - return '%s%s = %s%s' % (line.begin_tag, key_fragment.composer(), - val_fragment.composer(), line.end_tag) - first_part_len = len('%s = ' % key_fragment.composer()) + return '%s%s = %s%s%s' % (line.begin_tag, key_fragment.composer(), + val_fragment.composer(), postscript, + line.end_tag) + first_part_len = len('%s = ') + key_fragment.size remainder_len = length - first_part_len if remainder_len < 0: remainder_len = 0 - return '%s%s = %s%s' % ( + + return '%s%s = %s%s%s' % ( line.begin_tag, key_fragment.composer(), - val_fragment.composer(remainder_len), line.end_tag) + val_fragment.composer(remainder_len), postscript, line.end_tag) for k, v in result.items(): if k == 'properties': _str = "properties = %s" % _parse_properties(v) fragment = TaskResultFragment(text=_str) - line = TaskResultLine(fragments=[fragment], need_escape=False) + line = TaskResultLine(fragments=[fragment], need_escape=True) elif k != '__starstar': val_fragment = _parse_value(k, v) - key_fragment = TaskResultFragment(text=k) + key_fragment = TaskResultFragment(text=k, need_escape=True) line = TaskResultLine(fragments=[key_fragment, val_fragment], need_escape=False, composer=composer) lines.append(line) @@ -816,10 +826,10 @@ def task_result_to_html(result=None, exc_class=None, if total_abbr_len + line_len >= max_abbr_len: remainder_abbr_len = max_abbr_len - total_abbr_len + abbr_ret_str += line.composer(remainder_abbr_len, postscript=abbr_postscript) else: - remainder_abbr_len = None + abbr_ret_str += line.composer() total_abbr_lines += 1 total_abbr_len += line_len - abbr_ret_str += line.composer(remainder_abbr_len) - return full_ret_str, "%s %s" % (abbr_ret_str, abbr_postscript) + return full_ret_str, abbr_ret_str From 80e0410dbb0efba7515987105aecb7ff0f25e1d4 Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 08:00:22 +0000 Subject: [PATCH 6/10] lower the length limits and put the expansion link at the postscript end --- diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index 11e4572..90e0fb6 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -394,7 +394,7 @@ $value Result #if $abbr_result_text - +
$abbr_result_text
@@ -432,23 +432,30 @@ $value (function() { var abbr = document.getElementById('abbr-result'); var full = document.getElementById('full-result'); - t = document.getElementById('toggle-result'); + var link_to_show_abbr = document.getElementById('toggle-abbreviated-result'); + var link_to_show_full = document.getElementById('toggle-full-result'); full.style.display = 'none'; - t.style.display = 'block'; - t.text = 'Show complete result'; - t.onclick = function(e) { - if(abbr.style.display == 'none') { + abbr.style.display = 'block' + link_to_show_full.style.display = 'inline' + link_to_show_abbr.style.display = 'none' + var trigger = function(e) { + if (link_to_show_abbr.style.display == 'none') { + link_to_show_abbr.style.display = 'inline' + link_to_show_full.style.display = 'none' + abbr.style.display = 'none'; + full.style.display = 'block' + } + else { + link_to_show_abbr.style.display = 'none' + link_to_show_full.style.display = 'inline' abbr.style.display = 'block'; full.style.display = 'none' - t.text = 'show complete result'; - } - else { - abbr.style.display = 'none' - full.style.display = 'block'; - t.text = 'Show abbreviated result'; } return false; }; + + link_to_show_full.onclick = trigger + link_to_show_abbr.onclick = trigger })(); #end if diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 6ad08bb..8787f90 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -743,16 +743,31 @@ def task_result_to_html(result=None, exc_class=None, Returns: Tuple of full result and abbreviated result. """ - default_max_abbr_result_lines = 11 + default_max_abbr_result_lines = 10 default_max_abbr_result_len = 512 + if max_abbr_lines is None: max_abbr_lines = default_max_abbr_result_lines if max_abbr_len is None: max_abbr_len = default_max_abbr_result_len + + postscript_fragment = TaskResultFragment( + text='...', end_tag='', + begin_tag='' % ( + 'id="toggle-full-result"', + 'style="display: none;text-decoration:none;"')) + if abbr_postscript is None: - abbr_postscript = ' ...' - elif not abbr_postscript.startswith(' '): - abbr_postscript.startswith = ' %s' % abbr_postscript.startswith + abbr_postscript = postscript_fragment.composer() + elif isinstacne(abbr_postscript, TaskResultFragment): + abbr_postscript = abbr_postscript.composer() + elif isinstance(abbr_postscript, str): + abbr_postscript = abbr_postscript + else: + abbr_postscript = '...' + + if not abbr_postscript.startswith(' '): + abbr_postscript = ' %s' % abbr_postscript full_ret_str = '' abbr_ret_str = '' From b71d442f3a62942930cca05b76895ef441d1a851 Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 08:00:38 +0000 Subject: [PATCH 7/10] low the limits to 4 line and 400 chars --- diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 8787f90..f909f86 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -743,8 +743,8 @@ def task_result_to_html(result=None, exc_class=None, Returns: Tuple of full result and abbreviated result. """ - default_max_abbr_result_lines = 10 - default_max_abbr_result_len = 512 + default_max_abbr_result_lines = 5 + default_max_abbr_result_len = 400 if max_abbr_lines is None: max_abbr_lines = default_max_abbr_result_lines From a09947c61d36df1960afb1c496ee53cefad902f9 Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 08:00:52 +0000 Subject: [PATCH 8/10] fix the link to full results --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 30422ae..d9cd5a1 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -671,7 +671,7 @@ def taskinfo(environ, taskID): values['excClass'] = None full_result_text, abbr_result_text = kojiweb.util.task_result_to_html( - values['result'], values['excClass']) + values['result'], values['excClass'], abbr_postscript='...') values['full_result_text'] = full_result_text values['abbr_result_text'] = abbr_result_text diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index 90e0fb6..4d85e1c 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -398,14 +398,15 @@ $value
$abbr_result_text
+
$full_result_text
- #else + #else
$full_result_text
- #end if + #end if diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index f909f86..82e572f 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -759,7 +759,7 @@ def task_result_to_html(result=None, exc_class=None, if abbr_postscript is None: abbr_postscript = postscript_fragment.composer() - elif isinstacne(abbr_postscript, TaskResultFragment): + elif isinstance(abbr_postscript, TaskResultFragment): abbr_postscript = abbr_postscript.composer() elif isinstance(abbr_postscript, str): abbr_postscript = abbr_postscript From e70699e00924c58efec6bcd2a056418b643de040 Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 28 2016 08:01:10 +0000 Subject: [PATCH 9/10] add 'Show abbreviated results' also to bottom --- diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index 4d85e1c..3c54987 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -394,10 +394,10 @@ $value Result #if $abbr_result_text -
$abbr_result_text
+
$full_result_text From af2b17624ae597e20d53a65624e8e4ba60bd6fd8 Mon Sep 17 00:00:00 2001 From: Xibo Ning Date: Nov 29 2016 00:45:29 +0000 Subject: [PATCH 10/10] move link to the bottom --- diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index 3c54987..c81405a 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -397,11 +397,11 @@ $value
$abbr_result_text
- -
$full_result_text
+ + #else
$full_result_text