5------------------------------------------------------------------------------
6This file is part of grepros - grep for ROS bag files and live topics.
7Released under the BSD License.
12------------------------------------------------------------------------------
14## @namespace grepros.plugins.auto.html
18except ImportError:
import Queue
as queue
25from ... common
import ConsolePrinter, MatchMarkers
26from ... outputs
import RolloverSinkMixin, Sink, TextSinkMixin
27from ... vendor
import step
31 """Writes messages to an HTML file."""
34 FILE_EXTENSIONS = (
".htm",
".html")
37 TEMPLATE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"html.tpl")
43 DEFAULT_ARGS = dict(META=
False, WRITE_OPTIONS={}, HIGHLIGHT=
True, MATCH_WRAPPER=
None,
44 ORDERBY=
None, VERBOSE=
False, COLOR=
True, EMIT_FIELD=(), NOEMIT_FIELD=(),
45 MAX_FIELD_LINES=
None, START_LINE=
None, END_LINE=
None,
46 MAX_MESSAGE_LINES=
None, LINES_AROUND_MATCH=
None, MATCHED_FIELDS_ONLY=
False,
49 def __init__(self, args=None, **kwargs):
51 @param args arguments
as namespace
or dictionary, case-insensitive;
52 or a single path
as the name of HTML file to write
53 @param args.write name of HTML file to write,
54 will add counter like .2 to filename
if exists
55 @param args.write_options ```
56 {
"template": path to custom HTML template,
if any,
57 "overwrite": whether to overwrite existing file
59 "rollover-size": bytes limit
for individual output files,
60 "rollover-count": message limit
for individual output files,
61 "rollover-duration": time span limit
for individual output files,
62 as ROS duration
or convertible seconds,
63 "rollover-template": output filename template, supporting
64 strftime format codes like
"%H-%M-%S"
65 and "%(index)s" as output file index}
67 @param args.highlight highlight matched values (default true)
68 @param args.orderby
"topic" or "type" if any to group results by
69 @param args.color
False or "never" for not using colors
in replacements
70 @param args.emit_field message fields to emit
if not all
71 @param args.noemit_field message fields to skip
in output
72 @param args.max_field_lines maximum number of lines to output per field
73 @param args.start_line message line number to start output
from
74 @param args.end_line message line number to stop output at
75 @param args.max_message_lines maximum number of lines to output per message
76 @param args.lines_around_match number of message lines around matched fields to output
77 @param args.matched_fields_only output only the fields where match was found
78 @param args.wrap_width character width to wrap message YAML output at
79 @param args.match_wrapper string to wrap around matched values,
80 both sides
if one value, start
and end
if more than one,
81 or no wrapping
if zero values
82 @param args.meta whether to emit metainfo
83 @param args.verbose whether to emit debug information
84 @param kwargs any
and all arguments
as keyword overrides,
87 args = {"WRITE": str(args)}
if isinstance(args, common.PATH_TYPES)
else args
88 args = common.ensure_namespace(args, HtmlSink.DEFAULT_ARGS, **kwargs)
90 args.COLOR = bool(args.HIGHLIGHT)
93 RolloverSinkMixin.__init__(self, args)
94 TextSinkMixin.__init__(self, args)
95 self.
_queue = queue.Queue()
105 def emit(self, topic, msg, stamp=None, match=None, index=None):
106 """Writes message to output file."""
109 RolloverSinkMixin.ensure_rollover(self, topic, msg, stamp)
110 self.
_queue.put((topic, msg, stamp, match, index))
119 Returns whether write options are valid and ROS environment
is set
and file
is writable,
123 result = all([Sink.validate(self), TextSinkMixin.validate(self)])
124 if not RolloverSinkMixin.validate(self):
126 if self.
args.WRITE_OPTIONS.get(
"template")
and not os.path.isfile(self.
_template_path):
128 ConsolePrinter.error(
"Template does not exist: %s.", self.
_template_path)
129 if self.
args.WRITE_OPTIONS.get(
"overwrite")
not in (
None,
True,
False,
"true",
"false"):
130 ConsolePrinter.error(
"Invalid overwrite option for HTML: %r. "
131 "Choose one of {true, false}.",
132 self.
args.WRITE_OPTIONS[
"overwrite"])
134 if not common.verify_io(self.
args.WRITE,
"w"):
141 WRAPS, START = ((self.
args.MATCH_WRAPPER
or [
""]) * 2)[:2],
""
142 if self.
args.HIGHLIGHT: START = (
'<span class="match">' + step.escape_html(WRAPS[0]))
143 END = (step.escape_html(WRAPS[1]) +
'</span>')
if self.
args.HIGHLIGHT
else ""
145 MatchMarkers.END: END,
146 ConsolePrinter.STYLE_LOWLIGHT:
'<span class="lowlight">',
147 ConsolePrinter.STYLE_RESET:
'</span>'}
154 """Closes output file, if any, emits metainfo."""
160 super(HtmlSink, self).
close()
163 """Closes output file, if any."""
167 writer.is_alive()
and writer.join()
170 """Writes out any pending data to disk."""
174 """Returns message as formatted string, optionally highlighted for matches if configured."""
175 text = TextSinkMixin.format_message(self, msg, self.
args.HIGHLIGHT
and highlight)
176 text =
"".join(self.
_tag_repls.get(x)
or step.escape_html(x)
181 """Returns True if sink is configured to highlight matched values."""
182 return bool(self.
args.HIGHLIGHT)
185 """Writer-loop, streams HTML template to file."""
190 with open(self.
_template_path, encoding=
"utf-8")
as f: tpl = f.read()
191 template = step.Template(tpl, escape=
True, strip=
False, postprocess=convert_lf)
192 ns = dict(source=self.
source, sink=self, messages=self.
_produce(),
193 args=
None, timeline=
not self.
args.ORDERBY)
194 if main.CLI_ARGS: ns.update(args=main.CLI_ARGS)
196 if self.
args.VERBOSE:
198 action =
"Overwriting" if sz
and self.
_overwrite else "Creating"
202 template.stream(f, ns, buffer_size=0)
203 except Exception
as e:
209 """Yields messages from emit queue, as (topic, msg, stamp, match, index)."""
215 (topic, msg, stamp, match, index) = entry
216 topickey = api.TypeMeta.make(msg, topic).topickey
217 if self.
args.VERBOSE
and topickey
not in self.
_counts:
218 ConsolePrinter.debug(
"Adding topic %s in HTML output.", topic)
220 super(HtmlSink, self).
emit(topic, msg, stamp, match, index)
223 while self.
_queue.get_nowait()
or True: self.
_queue.task_done()
224 except queue.Empty:
pass
228 r"""Returns string with \r \n \r\n linefeeds replaced with given."""
229 return re.sub(
"(\r(?!\n))|((?<!\r)\n)|(\r\n)", newline, s)
234 """Adds HTML output format support."""
235 from ...
import plugins
236 plugins.add_write_format(
"html", HtmlSink,
"HTML", [
237 (
"template=/my/path.tpl",
"custom template to use for HTML output"),
238 (
"overwrite=true|false",
"overwrite existing file in HTML output\n"
239 "instead of appending unique counter (default false)")
240 ] + RolloverSinkMixin.get_write_options(
"HTML"))
241 plugins.add_output_label(
"HTML", [
"--emit-field",
"--no-emit-field",
"--matched-fields-only",
242 "--lines-around-match",
"--lines-per-field",
"--start-line",
243 "--end-line",
"--lines-per-message",
"--match-wrapper"])
246__all__ = [
"HtmlSink",
"init"]
Provides output file rollover by size, duration, or message count.
filename
Current output file path.
close_output(self)
Closes output file, if any.
validate(self)
Returns whether write options are valid, emits error if not, else populates options.
format_output_meta(self)
Returns output file metainfo string, with names and sizes and message/topic counts.
thread_excepthook(self, text, exc)
Handles exception, used by background threads.
source
inputs.Source instance bound to this sink
valid
Result of validate()
validate(self)
Returns whether sink prerequisites are met (like ROS environment set if LiveSink).
close(self)
Shuts down output, closing any files or connections.
Provides message formatting as text.
validate(self)
Returns whether arguments are valid, emits error if not, else populates options.
Writes messages to an HTML file.
__init__(self, args=None, **kwargs)
format_message(self, msg, highlight=False)
Returns message as formatted string, optionally highlighted for matches if configured.
close_output(self)
Closes output file, if any.
emit(self, topic, msg, stamp=None, match=None, index=None)
Writes message to output file.
int WRAP_WIDTH
Character wrap width for message YAML.
is_highlighting(self)
Returns True if sink is configured to highlight matched values.
flush(self)
Writes out any pending data to disk.
validate(self)
Returns whether write options are valid and ROS environment is set and file is writable,...
close(self)
Closes output file, if any, emits metainfo.
TEMPLATE_PATH
HTML template path.
convert_lf(s, newline=os.linesep)
Returns string with \r \r linefeeds replaced with given.
init(*_, **__)
Adds HTML output format support.