CSIT-1110: Integrate anomaly detection into PAL 55/12855/8
authorVratko Polak <vrpolak@cisco.com>
Fri, 8 Jun 2018 16:07:35 +0000 (18:07 +0200)
committerTibor Frank <tifrank@cisco.com>
Mon, 11 Jun 2018 08:30:21 +0000 (08:30 +0000)
+ Keep the original detection,
+ add the new one as subdirectory
  (both in source and in rendered tree).
- The new detection is not rebased over "Add dpdk mrr tests to trending".

New detection features:
+ Do not remove (nor detect) outliers.
+ Trend line shows the constant average within a group.
+ Anomaly circles are placed at the changed average.
+ Small bias against too similar averages.
+ Should be ready for moving the detection library out to pip.

Change-Id: I7ab1a92b79eeeed53ba65a071b1305e927816a89
Signed-off-by: Vratko Polak <vrpolak@cisco.com>
38 files changed:
resources/tools/presentation/new/conf.py [new file with mode: 0644]
resources/tools/presentation/new/conf_cpta/conf.py [new file with mode: 0644]
resources/tools/presentation/new/doc/pal_func_diagram.svg [new file with mode: 0644]
resources/tools/presentation/new/doc/pal_layers.svg [new file with mode: 0644]
resources/tools/presentation/new/doc/pal_lld.rst [new file with mode: 0644]
resources/tools/presentation/new/environment.py [new file with mode: 0644]
resources/tools/presentation/new/errors.py [new file with mode: 0644]
resources/tools/presentation/new/fdio.svg [new file with mode: 0644]
resources/tools/presentation/new/generator_CPTA.py [new file with mode: 0644]
resources/tools/presentation/new/generator_files.py [new file with mode: 0644]
resources/tools/presentation/new/generator_plots.py [new file with mode: 0644]
resources/tools/presentation/new/generator_report.py [new file with mode: 0644]
resources/tools/presentation/new/generator_tables.py [new file with mode: 0644]
resources/tools/presentation/new/input_data_files.py [new file with mode: 0644]
resources/tools/presentation/new/input_data_parser.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/AbstractGroupClassifier.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/AbstractGroupMetadata.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/AvgStdevMetadata.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/AvgStdevMetadataFactory.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/BitCountingClassifier.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/BitCountingGroup.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/BitCountingGroupList.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/BitCountingMetadata.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/BitCountingMetadataFactory.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/ClassifiedBitCountingMetadata.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/ClassifiedMetadataFactory.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/RunGroup.py [new file with mode: 0644]
resources/tools/presentation/new/jumpavg/__init__.py [new file with mode: 0644]
resources/tools/presentation/new/pal.py [new file with mode: 0644]
resources/tools/presentation/new/requirements.txt [new file with mode: 0644]
resources/tools/presentation/new/run_cpta.sh [new file with mode: 0755]
resources/tools/presentation/new/run_report.sh [new file with mode: 0755]
resources/tools/presentation/new/specification.yaml [new file with mode: 0644]
resources/tools/presentation/new/specification_CPTA.yaml [new file with mode: 0644]
resources/tools/presentation/new/specification_parser.py [new file with mode: 0644]
resources/tools/presentation/new/static_content.py [new file with mode: 0644]
resources/tools/presentation/new/utils.py [new file with mode: 0644]
resources/tools/presentation/run_cpta.sh

diff --git a/resources/tools/presentation/new/conf.py b/resources/tools/presentation/new/conf.py
new file mode 100644 (file)
index 0000000..84890d9
--- /dev/null
@@ -0,0 +1,239 @@
+# -*- coding: utf-8 -*-
+#
+# CSIT report documentation build configuration file
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinxcontrib.programoutput',
+              'sphinx.ext.ifconfig']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source file names.
+# You can specify multiple suffix as a list of string:
+#
+source_suffix = ['.rst', '.md']
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'FD.io CSIT'
+copyright = u'2018, FD.io'
+author = u'FD.io CSIT'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+# version = u''
+# The full version, including alpha/beta/rc tags.
+# release = u''
+
+rst_epilog = """
+.. |release-1| replace:: {prev_release}
+.. |srelease| replace:: {srelease}
+.. |vpp-release| replace:: VPP-{vpprelease} release
+.. |vpp-release-1| replace:: VPP-{vpp_prev_release} release
+.. |dpdk-release| replace:: DPDK {dpdkrelease}
+.. |trex-release| replace:: TRex {trex_version}
+.. |virl-image-ubuntu| replace:: {csit_ubuntu_ver}
+.. |virl-image-centos| replace:: {csit_centos_ver}
+
+.. _pdf version of this report: https://docs.fd.io/csit/{release}/report/_static/archive/csit_{release}.pdf
+.. _tag documentation rst file: https://git.fd.io/csit/tree/docs/tag_documentation.rst?h={release}
+.. _TRex intallation: https://git.fd.io/csit/tree/resources/tools/trex/trex_installer.sh?h={release}
+.. _TRex driver: https://git.fd.io/csit/tree/resources/tools/trex/trex_stateless_profile.py?h={release}
+.. _VIRL topologies directory: https://git.fd.io/csit/tree/resources/tools/virl/topologies/?h={release}
+.. _VIRL ubuntu images lists: https://git.fd.io/csit/tree/resources/tools/disk-image-builder/ubuntu/lists/?h={release}
+.. _VIRL centos images lists: https://git.fd.io/csit/tree/resources/tools/disk-image-builder/centos/lists/?h={release}
+.. _VIRL nested: https://git.fd.io/csit/tree/resources/tools/disk-image-builder/nested/?h={release}
+.. _CSIT Honeycomb Functional Tests Documentation: https://docs.fd.io/csit/{release}/doc/tests.vpp.func.honeycomb.html
+.. _CSIT Honeycomb Performance Tests Documentation: https://docs.fd.io/csit/{release}/doc/tests.vpp.perf.honeycomb.html
+.. _CSIT DPDK Performance Tests Documentation: https://docs.fd.io/csit/{release}/doc/tests.dpdk.perf.html
+.. _CSIT VPP Functional Tests Documentation: https://docs.fd.io/csit/{release}/doc/tests.vpp.func.html
+.. _CSIT VPP Performance Tests Documentation: https://docs.fd.io/csit/{release}/doc/tests.vpp.perf.html
+.. _CSIT NSH_SFC Functional Tests Documentation: https://docs.fd.io/csit/{release}/doc/tests.nsh_sfc.func.html
+.. _VPP test framework documentation: https://docs.fd.io/vpp/{vpprelease}/vpp_make_test/html/
+.. _FD.io test executor vpp performance jobs: https://jenkins.fd.io/view/csit/job/csit-vpp-perf-{srelease}-all
+.. _FD.io test executor ligato performance jobs: https://jenkins.fd.io/job/csit-ligato-perf-{srelease}-all
+.. _FD.io test executor vpp functional jobs: https://jenkins.fd.io/view/csit/job/csit-vpp-functional-{srelease}-ubuntu1604-virl/lastSuccessfulBuild
+.. _FD.io test executor dpdk performance jobs: https://jenkins.fd.io/view/csit/job/csit-dpdk-perf-{srelease}-all
+.. _FD.io test executor Honeycomb functional jobs: https://jenkins.fd.io/view/csit/job/hc2vpp-csit-integration-{srelease}-ubuntu1604/lastSuccessfulBuild
+.. _FD.io test executor honeycomb performance jobs: https://jenkins.fd.io/view/hc2vpp/job/hc2vpp-csit-perf-master-ubuntu1604/lastSuccessfulBuild
+.. _FD.io test executor NSH_SFC functional jobs: https://jenkins.fd.io/view/csit/job/csit-nsh_sfc-verify-func-{srelease}-ubuntu1604-virl/lastSuccessfulBuild
+.. _FD.io VPP compile job: https://jenkins.fd.io/view/vpp/job/vpp-merge-{srelease}-ubuntu1604/
+.. _FD.io DPDK compile job: https://jenkins.fd.io/view/deb-dpdk/job/deb_dpdk-merge-{sdpdkrelease}-ubuntu1604/
+.. _CSIT Testbed Setup: https://git.fd.io/csit/tree/resources/tools/testbed-setup/README.md?h={release}
+""".format(release='rls1804',
+           prev_release='rls1801',
+           srelease='1804',
+           vpprelease='18.04',
+           vpp_prev_release='18.01',
+           dpdkrelease='18.02',
+           sdpdkrelease='1802',
+           trex_version='v2.35',
+           csit_ubuntu_ver='csit-ubuntu-16.04.1_2018-03-07_2.1',
+           csit_centos_ver='csit-centos-7.4-1711_2018-03-20_1.9')
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = 'en'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_theme_path = ['env/lib/python2.7/site-packages/sphinx_rtd_theme']
+
+# html_static_path = ['_build/_static']
+html_static_path = ['_tmp/src/_static']
+
+html_context = {
+    'css_files': [
+        '_static/theme_overrides.css',  # overrides for wide tables in RTD theme
+        ],
+    }
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_engine = 'pdflatex'
+
+latex_elements = {
+     # The paper size ('letterpaper' or 'a4paper').
+     #
+     'papersize': 'a4paper',
+
+     # The font size ('10pt', '11pt' or '12pt').
+     #
+     #'pointsize': '10pt',
+
+     # Additional stuff for the LaTeX preamble.
+     #
+     'preamble': r'''
+         \usepackage{pdfpages}
+         \usepackage{svg}
+         \usepackage{charter}
+         \usepackage[defaultsans]{lato}
+         \usepackage{inconsolata}
+         \usepackage{csvsimple}
+         \usepackage{longtable}
+         \usepackage{booktabs}
+     ''',
+
+     # Latex figure (float) alignment
+     #
+     'figure_align': 'H',
+
+     # Latex font setup
+     #
+     'fontpkg': r'''
+         \renewcommand{\familydefault}{\sfdefault}
+     ''',
+
+     # Latex other setup
+     #
+     'extraclassoptions': 'openany',
+     'sphinxsetup': r'''
+         TitleColor={RGB}{225,38,40},
+         InnerLinkColor={RGB}{62,62,63},
+         OuterLinkColor={RGB}{225,38,40},
+         shadowsep=0pt,
+         shadowsize=0pt,
+         shadowrule=0pt
+     '''
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'csit.tex', u'CSIT REPORT',
+     u'', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
+# latex_logo = 'fdio.pdf'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#
+# latex_use_parts = True
+
+# If true, show page references after internal links.
+#
+latex_show_pagerefs = True
+
+# If true, show URL addresses after external links.
+#
+latex_show_urls = 'footnote'
+
+# Documents to append as an appendix to all manuals.
+#
+# latex_appendices = []
+
+# It false, will not define \strong, \code,    itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
+# If false, no module index is generated.
+#
+# latex_domain_indices = True
diff --git a/resources/tools/presentation/new/conf_cpta/conf.py b/resources/tools/presentation/new/conf_cpta/conf.py
new file mode 100644 (file)
index 0000000..4eb51d8
--- /dev/null
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+#
+# CSIT report documentation build configuration file
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinxcontrib.programoutput',
+              'sphinx.ext.ifconfig']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+source_suffix = ['.rst', '.md']
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'FD.io CSIT'
+copyright = u'2018, FD.io'
+author = u'FD.io CSIT'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+#version = u''
+# The full version, including alpha/beta/rc tags.
+#release = u''
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = 'en'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_theme_path = ['env/lib/python2.7/site-packages/sphinx_rtd_theme']
+
+# html_static_path = ['_build/_static']
+html_static_path = ['../_tmp/src/_static']
+
+html_context = {
+    'css_files': [
+        '_static/theme_overrides.css',  # overrides for wide tables in RTD theme
+        ],
+    }
+
+# If false, no module index is generated.
+html_domain_indices = True
+
+# If false, no index is generated.
+html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+html_split_index = False
diff --git a/resources/tools/presentation/new/doc/pal_func_diagram.svg b/resources/tools/presentation/new/doc/pal_func_diagram.svg
new file mode 100644 (file)
index 0000000..14f5960
--- /dev/null
@@ -0,0 +1,1413 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<svg version="1.2" baseProfile="tiny" width="210.06mm" height="270.76mm" viewBox="0 0 21006 27076" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
+ <defs class="ClipPathGroup">
+  <clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
+   <rect x="0" y="0" width="21006" height="27076"/>
+  </clipPath>
+ </defs>
+ <defs>
+  <font id="EmbeddedFont_1" horiz-adv-x="2048">
+   <font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
+   <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
+   <glyph unicode="x" horiz-adv-x="1033" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
+   <glyph unicode="u" horiz-adv-x="901" d="M 314,1082 L 314,396 C 314,343 318,299 326,264 333,229 346,200 363,179 380,157 403,142 432,133 460,124 495,119 537,119 580,119 618,127 653,142 687,157 716,178 741,207 765,235 784,270 797,312 810,353 817,401 817,455 L 817,1082 997,1082 997,231 C 997,208 997,185 998,160 998,135 998,111 999,89 1000,66 1000,47 1001,31 1002,15 1002,5 1003,0 L 833,0 C 832,3 832,12 831,27 830,42 830,59 829,78 828,97 827,116 826,136 825,155 825,172 825,185 L 822,185 C 805,154 786,125 765,100 744,75 720,53 693,36 666,18 634,4 599,-6 564,-15 523,-20 476,-20 416,-20 364,-13 321,2 278,17 242,39 214,70 186,101 166,140 153,188 140,236 133,294 133,361 L 133,1082 314,1082 Z"/>
+   <glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 527,1 499,-5 471,-10 442,-14 409,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 467,127 484,128 501,131 517,134 535,137 554,141 L 554,8 Z"/>
+   <glyph unicode="s" horiz-adv-x="927" d="M 950,299 C 950,248 940,203 921,164 901,124 872,91 835,64 798,37 752,16 698,2 643,-13 581,-20 511,-20 448,-20 392,-15 342,-6 291,4 247,20 209,41 171,62 139,91 114,126 88,161 69,203 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 550,117 585,120 618,125 650,130 678,140 701,153 724,166 743,183 756,205 769,226 775,253 775,285 775,318 767,345 752,366 737,387 715,404 688,418 661,432 628,444 589,455 550,465 507,476 460,489 417,500 374,513 331,527 288,541 250,560 216,583 181,606 153,634 132,668 111,702 100,745 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 763,842 752,866 736,885 720,904 701,919 678,931 655,942 630,951 602,956 573,961 544,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,785 282,761 297,742 311,723 331,707 357,694 382,681 413,669 449,660 485,650 525,640 568,629 597,622 626,614 656,606 686,597 715,587 744,576 772,564 799,550 824,535 849,519 870,500 889,478 908,456 923,430 934,401 945,372 950,338 950,299 Z"/>
+   <glyph unicode="r" horiz-adv-x="556" d="M 142,0 L 142,830 C 142,853 142,876 142,900 141,923 141,946 140,968 139,990 139,1011 138,1030 137,1049 137,1067 136,1082 L 306,1082 C 307,1067 308,1049 309,1030 310,1010 311,990 312,969 313,948 313,929 314,910 314,891 314,874 314,861 L 318,861 C 331,902 344,938 359,969 373,999 390,1024 409,1044 428,1063 451,1078 478,1088 505,1097 537,1102 575,1102 590,1102 604,1101 617,1099 630,1096 641,1094 648,1092 L 648,927 C 636,930 622,933 606,935 590,936 572,937 552,937 511,937 476,928 447,909 418,890 394,865 376,832 357,799 344,759 335,714 326,668 322,618 322,564 L 322,0 142,0 Z"/>
+   <glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,464 1046,388 1033,319 1020,250 998,190 967,140 936,90 895,51 844,23 793,-6 730,-20 655,-20 578,-20 510,-5 452,24 394,53 350,101 319,168 L 314,168 C 315,167 315,161 316,150 316,139 316,126 317,110 317,94 317,76 318,57 318,37 318,17 318,-2 L 318,-425 138,-425 138,861 C 138,887 138,912 138,936 137,960 137,982 136,1002 135,1021 135,1038 134,1052 133,1066 133,1076 132,1082 L 306,1082 C 307,1080 308,1073 309,1061 310,1049 311,1035 312,1018 313,1001 314,982 315,963 316,944 316,925 316,908 L 320,908 C 337,943 356,972 377,997 398,1021 423,1041 450,1057 477,1072 508,1084 542,1091 575,1098 613,1101 655,1101 730,1101 793,1088 844,1061 895,1034 936,997 967,949 998,900 1020,842 1033,774 1046,705 1053,629 1053,546 Z M 864,542 C 864,609 860,668 852,720 844,772 830,816 811,852 791,888 765,915 732,934 699,953 658,962 609,962 569,962 531,956 496,945 461,934 430,912 404,880 377,848 356,804 341,748 326,691 318,618 318,528 318,451 324,387 337,334 350,281 368,238 393,205 417,172 447,149 483,135 519,120 560,113 607,113 657,113 699,123 732,142 765,161 791,189 811,226 830,263 844,308 852,361 860,414 864,474 864,542 Z"/>
+   <glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 490,-20 422,-9 363,14 304,37 254,71 213,118 172,165 140,223 119,294 97,364 86,447 86,542 86,915 248,1102 571,1102 655,1102 728,1090 789,1067 850,1044 900,1009 939,962 978,915 1006,857 1025,787 1044,717 1053,635 1053,542 Z M 864,542 C 864,626 858,695 845,750 832,805 813,848 788,881 763,914 732,937 696,950 660,963 619,969 574,969 528,969 487,962 450,949 413,935 381,912 355,879 329,846 309,802 296,747 282,692 275,624 275,542 275,458 282,389 297,334 312,279 332,235 358,202 383,169 414,146 449,133 484,120 522,113 563,113 609,113 651,120 688,133 725,146 757,168 783,201 809,234 829,278 843,333 857,388 864,458 864,542 Z"/>
+   <glyph unicode="n" horiz-adv-x="900" d="M 825,0 L 825,686 C 825,739 821,783 814,818 806,853 793,882 776,904 759,925 736,941 708,950 679,959 644,963 602,963 559,963 521,956 487,941 452,926 423,904 399,876 374,847 355,812 342,771 329,729 322,681 322,627 L 322,0 142,0 142,851 C 142,874 142,898 142,923 141,948 141,971 140,994 139,1016 139,1035 138,1051 137,1067 137,1077 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 312,966 313,947 314,927 314,910 314,897 L 317,897 C 334,928 353,957 374,982 395,1007 419,1029 446,1047 473,1064 505,1078 540,1088 575,1097 616,1102 663,1102 723,1102 775,1095 818,1080 861,1065 897,1043 925,1012 953,981 974,942 987,894 1000,845 1006,788 1006,721 L 1006,0 825,0 Z"/>
+   <glyph unicode="m" horiz-adv-x="1456" d="M 768,0 L 768,686 C 768,739 765,783 758,818 751,853 740,882 725,904 709,925 688,941 663,950 638,959 607,963 570,963 532,963 498,956 467,941 436,926 410,904 389,876 367,847 350,812 339,771 327,729 321,681 321,627 L 321,0 142,0 142,851 C 142,874 142,898 142,923 141,948 141,971 140,994 139,1016 139,1035 138,1051 137,1067 137,1077 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 312,966 313,947 314,927 314,910 314,897 L 317,897 C 333,928 350,957 369,982 388,1007 410,1029 435,1047 460,1064 488,1078 521,1088 553,1097 590,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 946,928 964,957 984,982 1004,1007 1027,1029 1054,1047 1081,1064 1111,1078 1144,1088 1177,1097 1215,1102 1258,1102 1313,1102 1360,1095 1400,1080 1439,1065 1472,1043 1497,1012 1522,981 1541,942 1553,894 1565,845 1571,788 1571,721 L 1571,0 1393,0 1393,686 C 1393,739 1390,783 1383,818 1376,853 1365,882 1350,904 1334,925 1313,941 1288,950 1263,959 1232,963 1195,963 1157,963 1123,956 1092,942 1061,927 1035,906 1014,878 992,850 975,815 964,773 952,731 946,682 946,627 L 946,0 768,0 Z"/>
+   <glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
+   <glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
+   <glyph unicode="g" horiz-adv-x="954" d="M 548,-425 C 486,-425 431,-419 383,-406 335,-393 294,-375 260,-352 226,-328 198,-300 177,-267 156,-234 140,-198 131,-158 L 312,-132 C 324,-182 351,-220 392,-248 433,-274 486,-288 553,-288 594,-288 631,-282 664,-271 697,-260 726,-241 749,-217 772,-191 790,-159 803,-119 816,-79 822,-30 822,27 L 822,201 820,201 C 807,174 790,148 771,123 751,98 727,75 699,56 670,37 637,21 600,10 563,-2 520,-8 472,-8 403,-8 345,4 296,27 247,50 207,84 176,130 145,176 122,233 108,302 93,370 86,449 86,539 86,626 93,704 108,773 122,842 145,901 178,950 210,998 252,1035 304,1061 355,1086 418,1099 492,1099 569,1099 635,1082 692,1047 748,1012 791,962 822,897 L 824,897 C 824,914 825,932 826,953 827,974 828,993 829,1012 830,1030 831,1046 832,1059 833,1072 835,1080 836,1082 L 1007,1082 C 1006,1076 1006,1066 1005,1052 1004,1037 1004,1020 1003,1000 1002,980 1002,958 1002,934 1001,909 1001,884 1001,858 L 1001,31 C 1001,-120 964,-234 890,-311 815,-387 701,-425 548,-425 Z M 822,541 C 822,616 814,681 798,735 781,788 760,832 733,866 706,900 676,925 642,941 607,957 572,965 536,965 490,965 451,957 418,941 385,925 357,900 336,866 314,831 298,787 288,734 277,680 272,616 272,541 272,463 277,398 288,345 298,292 314,249 335,216 356,183 383,160 416,146 449,132 488,125 533,125 569,125 604,133 639,148 673,163 704,188 731,221 758,254 780,297 797,350 814,403 822,466 822,541 Z"/>
+   <glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1243 185,1280 192,1314 199,1347 213,1377 233,1402 252,1427 279,1446 313,1461 347,1475 391,1482 445,1482 466,1482 489,1481 512,1479 535,1477 555,1474 572,1470 L 572,1333 C 561,1335 548,1337 533,1339 518,1340 504,1341 492,1341 465,1341 444,1337 427,1330 410,1323 396,1312 387,1299 377,1285 370,1268 367,1248 363,1228 361,1205 361,1179 L 361,1082 572,1082 572,951 361,951 Z"/>
+   <glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,446 282,394 294,347 305,299 323,258 348,224 372,189 403,163 441,144 479,125 525,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 1008,206 992,176 972,146 951,115 924,88 890,64 856,39 814,19 763,4 712,-12 650,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,649 100,735 125,806 150,876 185,933 229,977 273,1021 324,1053 383,1073 442,1092 504,1102 571,1102 662,1102 738,1087 799,1058 860,1029 909,988 946,937 983,885 1009,824 1025,754 1040,684 1048,608 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 538,969 507,964 474,955 441,945 410,928 382,903 354,878 330,845 311,803 292,760 281,706 278,641 L 862,641 Z"/>
+   <glyph unicode="d" horiz-adv-x="954" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 C 823,921 823,931 823,946 822,960 822,975 822,991 821,1006 821,1021 821,1035 821,1049 821,1059 821,1065 L 821,1484 1001,1484 1001,223 C 1001,197 1001,172 1002,148 1002,124 1002,102 1003,82 1004,62 1004,45 1005,31 1006,16 1006,6 1007,0 L 835,0 C 834,7 833,16 832,29 831,41 830,55 829,71 828,87 827,104 826,122 825,139 825,157 825,174 L 821,174 Z M 275,542 C 275,467 280,403 289,350 298,297 313,253 334,219 355,184 381,159 413,143 445,127 484,119 530,119 577,119 619,127 656,142 692,157 722,182 747,217 771,251 789,296 802,351 815,406 821,474 821,554 821,631 815,696 802,749 789,802 771,844 746,877 721,910 691,933 656,948 620,962 579,969 532,969 488,969 450,961 418,946 386,931 359,906 338,872 317,838 301,794 291,740 280,685 275,619 275,542 Z"/>
+   <glyph unicode="c" horiz-adv-x="875" d="M 275,546 C 275,484 280,427 289,375 298,323 313,278 334,241 355,203 384,174 419,153 454,132 497,122 548,122 612,122 666,139 709,173 752,206 778,258 788,328 L 970,328 C 964,283 951,239 931,197 911,155 884,118 850,86 815,54 773,28 724,9 675,-10 618,-20 553,-20 468,-20 396,-6 337,23 278,52 230,91 193,142 156,192 129,251 112,320 95,388 87,462 87,542 87,615 93,679 105,735 117,790 134,839 156,881 177,922 203,957 232,986 261,1014 293,1037 328,1054 362,1071 398,1083 436,1091 474,1098 512,1102 551,1102 612,1102 666,1094 713,1077 760,1060 801,1038 836,1009 870,980 898,945 919,906 940,867 955,824 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 495,961 452,953 418,936 383,919 355,893 334,859 313,824 298,781 289,729 280,677 275,616 275,546 Z"/>
+   <glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,151 316,133 315,114 314,95 313,78 312,62 311,46 310,32 309,21 308,10 307,3 306,0 L 132,0 C 133,6 133,16 134,31 135,45 135,62 136,82 137,102 137,124 138,148 138,172 138,197 138,223 L 138,1484 318,1484 318,1061 C 318,1041 318,1022 318,1004 317,985 317,969 316,955 315,938 315,923 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,615 859,679 850,732 841,785 826,829 805,864 784,898 758,923 726,939 694,955 655,963 609,963 562,963 520,955 484,940 447,925 417,900 393,866 368,832 350,787 337,732 324,677 318,609 318,529 318,452 324,387 337,334 350,281 368,239 393,206 417,173 447,149 483,135 519,120 560,113 607,113 651,113 689,121 721,136 753,151 780,176 801,210 822,244 838,288 849,343 859,397 864,463 864,540 Z"/>
+   <glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,124 87,203 87,303 87,375 101,434 128,480 155,526 190,562 234,588 277,614 327,632 383,642 439,652 496,657 554,657 L 797,657 797,717 C 797,762 792,800 783,832 774,863 759,889 740,908 721,928 697,942 668,951 639,960 604,965 565,965 530,965 499,963 471,958 443,953 419,944 398,931 377,918 361,900 348,878 335,855 327,827 323,793 L 135,810 C 142,853 154,892 173,928 192,963 218,994 253,1020 287,1046 330,1066 382,1081 433,1095 496,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1090,111 1100,112 1110,113 1120,114 1130,116 1139,118 L 1139,6 C 1116,1 1094,-3 1072,-6 1049,-9 1025,-10 1000,-10 966,-10 937,-5 913,4 888,13 868,26 853,45 838,63 826,86 818,113 810,140 805,171 803,207 L 797,207 C 778,172 757,141 734,113 711,85 684,61 653,42 622,22 588,7 549,-4 510,-15 465,-20 414,-20 Z M 455,115 C 512,115 563,125 606,146 649,167 684,194 713,226 741,259 762,294 776,332 790,371 797,408 797,443 L 797,531 600,531 C 556,531 514,528 475,522 435,517 400,506 370,489 340,472 316,449 299,418 281,388 272,349 272,300 272,241 288,195 320,163 351,131 396,115 455,115 Z"/>
+   <glyph unicode="_" horiz-adv-x="1191" d="M -31,-407 L -31,-277 1162,-277 1162,-407 -31,-407 Z"/>
+   <glyph unicode="Y" horiz-adv-x="1244" d="M 777,584 L 777,0 587,0 587,584 45,1409 255,1409 684,738 1111,1409 1321,1409 777,584 Z"/>
+   <glyph unicode="T" horiz-adv-x="1139" d="M 720,1253 L 720,0 530,0 530,1253 46,1253 46,1409 1204,1409 1204,1253 720,1253 Z"/>
+   <glyph unicode="S" horiz-adv-x="1139" d="M 1272,389 C 1272,330 1261,275 1238,225 1215,175 1179,132 1131,96 1083,59 1023,31 950,11 877,-10 790,-20 690,-20 515,-20 378,11 280,72 182,133 120,222 93,338 L 278,375 C 287,338 302,305 321,275 340,245 367,219 400,198 433,176 473,159 522,147 571,135 629,129 697,129 754,129 806,134 853,144 900,153 941,168 975,188 1009,208 1036,234 1055,266 1074,297 1083,335 1083,379 1083,425 1073,462 1052,491 1031,520 1001,543 963,562 925,581 880,596 827,609 774,622 716,635 652,650 613,659 573,668 534,679 494,689 456,701 420,716 383,730 349,747 317,766 285,785 257,809 234,836 211,863 192,894 179,930 166,965 159,1006 159,1053 159,1120 173,1177 200,1225 227,1272 264,1311 312,1342 360,1373 417,1395 482,1409 547,1423 618,1430 694,1430 781,1430 856,1423 918,1410 980,1396 1032,1375 1075,1348 1118,1321 1152,1287 1178,1247 1203,1206 1224,1159 1239,1106 L 1051,1073 C 1042,1107 1028,1137 1011,1164 993,1191 970,1213 941,1231 912,1249 878,1263 837,1272 796,1281 747,1286 692,1286 627,1286 572,1280 528,1269 483,1257 448,1241 421,1221 394,1201 374,1178 363,1151 351,1124 345,1094 345,1063 345,1021 356,987 377,960 398,933 426,910 462,892 498,874 540,859 587,847 634,835 685,823 738,811 781,801 825,791 868,781 911,770 952,758 991,744 1030,729 1067,712 1102,693 1136,674 1166,650 1191,622 1216,594 1236,561 1251,523 1265,485 1272,440 1272,389 Z"/>
+   <glyph unicode="R" horiz-adv-x="1218" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 911,1409 982,1400 1044,1382 1105,1363 1157,1337 1199,1302 1241,1267 1273,1225 1295,1175 1317,1125 1328,1069 1328,1006 1328,961 1322,917 1309,874 1296,831 1275,791 1247,755 1219,719 1183,688 1140,662 1097,636 1045,618 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1047 1129,1084 1114,1115 1099,1146 1078,1173 1050,1194 1022,1215 988,1230 948,1241 908,1251 863,1256 812,1256 L 359,1256 359,736 820,736 C 875,736 922,743 962,757 1002,770 1035,789 1061,813 1086,837 1105,865 1118,898 1130,931 1136,966 1136,1004 Z"/>
+   <glyph unicode="P" horiz-adv-x="1086" d="M 1258,985 C 1258,924 1248,867 1228,814 1207,761 1177,715 1137,676 1096,637 1046,606 985,583 924,560 854,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 844,1409 917,1399 979,1379 1041,1358 1093,1330 1134,1293 1175,1256 1206,1211 1227,1159 1248,1106 1258,1048 1258,985 Z M 1066,983 C 1066,1072 1039,1140 984,1187 929,1233 847,1256 738,1256 L 359,1256 359,700 746,700 C 856,700 937,724 989,773 1040,822 1066,892 1066,983 Z"/>
+   <glyph unicode="M" horiz-adv-x="1324" d="M 1366,0 L 1366,940 C 1366,974 1366,1009 1367,1044 1368,1079 1369,1112 1370,1141 1371,1175 1373,1208 1375,1240 1366,1206 1356,1172 1346,1139 1337,1110 1328,1080 1318,1048 1307,1015 1297,986 1287,960 L 923,0 789,0 420,960 C 416,970 412,982 408,995 403,1008 399,1023 394,1038 389,1053 384,1068 379,1084 374,1099 369,1115 364,1130 353,1165 342,1202 331,1240 332,1203 333,1166 334,1129 335,1098 336,1065 337,1031 338,996 338,966 338,940 L 338,0 168,0 168,1409 419,1409 794,432 C 799,419 804,402 811,381 818,360 824,338 830,316 836,294 842,273 847,254 852,234 855,219 857,208 859,219 863,234 868,254 873,274 880,295 887,317 894,339 900,360 907,381 914,402 920,419 925,432 L 1293,1409 1538,1409 1538,0 1366,0 Z"/>
+   <glyph unicode="L" horiz-adv-x="900" d="M 168,0 L 168,1409 359,1409 359,156 1071,156 1071,0 168,0 Z"/>
+   <glyph unicode="I" horiz-adv-x="186" d="M 189,0 L 189,1409 380,1409 380,0 189,0 Z"/>
+   <glyph unicode="F" horiz-adv-x="1006" d="M 359,1253 L 359,729 1145,729 1145,571 359,571 359,0 168,0 168,1409 1169,1409 1169,1253 359,1253 Z"/>
+   <glyph unicode="D" horiz-adv-x="1218" d="M 1381,719 C 1381,602 1363,498 1328,409 1293,319 1244,244 1183,184 1122,123 1049,78 966,47 882,16 792,0 695,0 L 168,0 168,1409 634,1409 C 743,1409 843,1396 935,1369 1026,1342 1105,1300 1171,1244 1237,1187 1289,1116 1326,1029 1363,942 1381,839 1381,719 Z M 1189,719 C 1189,814 1175,896 1148,964 1121,1031 1082,1087 1033,1130 984,1173 925,1205 856,1226 787,1246 712,1256 630,1256 L 359,1256 359,153 673,153 C 747,153 816,165 879,189 942,213 996,249 1042,296 1088,343 1124,402 1150,473 1176,544 1189,626 1189,719 Z"/>
+   <glyph unicode="A" horiz-adv-x="1350" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 768,1026 C 757,1053 747,1080 738,1107 728,1134 719,1159 712,1182 705,1204 699,1223 694,1238 689,1253 686,1262 685,1265 684,1262 681,1252 676,1237 671,1222 665,1203 658,1180 650,1157 641,1132 632,1105 622,1078 612,1051 602,1024 L 422,561 949,561 768,1026 Z"/>
+   <glyph unicode="4" horiz-adv-x="1033" d="M 881,319 L 881,0 711,0 711,319 47,319 47,459 692,1409 881,1409 881,461 1079,461 1079,319 881,319 Z M 711,1206 C 710,1203 706,1196 701,1187 696,1177 690,1166 683,1154 676,1142 670,1130 663,1118 656,1105 649,1095 644,1087 L 283,555 C 280,550 275,543 269,534 262,525 256,517 249,508 242,499 236,490 229,481 222,472 217,466 213,461 L 711,461 711,1206 Z"/>
+   <glyph unicode="3" horiz-adv-x="980" d="M 1049,389 C 1049,324 1039,267 1018,216 997,165 966,123 926,88 885,53 835,26 776,8 716,-11 648,-20 571,-20 484,-20 410,-9 351,13 291,34 242,63 203,99 164,134 135,175 116,221 97,266 84,313 78,362 L 264,379 C 269,342 279,308 294,277 308,246 327,220 352,198 377,176 407,159 443,147 479,135 522,129 571,129 662,129 733,151 785,196 836,241 862,307 862,395 862,447 851,489 828,521 805,552 776,577 742,595 707,612 670,624 630,630 589,636 552,639 518,639 L 416,639 416,795 514,795 C 548,795 583,799 620,806 657,813 690,825 721,844 751,862 776,887 796,918 815,949 825,989 825,1038 825,1113 803,1173 759,1217 714,1260 648,1282 561,1282 482,1282 418,1262 369,1221 320,1180 291,1123 283,1049 L 102,1063 C 109,1125 126,1179 153,1225 180,1271 214,1309 255,1340 296,1370 342,1393 395,1408 448,1423 504,1430 563,1430 642,1430 709,1420 766,1401 823,1381 869,1354 905,1321 941,1287 968,1247 985,1202 1002,1157 1010,1108 1010,1057 1010,1016 1004,977 993,941 982,905 964,873 940,844 916,815 886,791 849,770 812,749 767,734 715,723 L 715,719 C 772,713 821,700 863,681 905,661 940,636 967,607 994,578 1015,544 1029,507 1042,470 1049,430 1049,389 Z"/>
+   <glyph unicode="2" horiz-adv-x="927" d="M 103,0 L 103,127 C 137,205 179,274 228,334 277,393 328,447 382,496 436,544 490,589 543,630 596,671 643,713 686,754 729,795 763,839 790,884 816,929 829,981 829,1038 829,1078 823,1113 811,1144 799,1174 782,1199 759,1220 736,1241 709,1256 678,1267 646,1277 611,1282 572,1282 536,1282 502,1277 471,1267 439,1257 411,1242 386,1222 361,1202 341,1177 326,1148 310,1118 300,1083 295,1044 L 111,1061 C 117,1112 131,1159 153,1204 175,1249 205,1288 244,1322 283,1355 329,1382 384,1401 438,1420 501,1430 572,1430 642,1430 704,1422 759,1405 814,1388 860,1364 898,1331 935,1298 964,1258 984,1210 1004,1162 1014,1107 1014,1044 1014,997 1006,952 989,909 972,866 949,826 921,787 892,748 859,711 822,675 785,639 746,604 705,570 664,535 623,501 582,468 541,434 502,400 466,366 429,332 397,298 368,263 339,228 317,191 301,153 L 1036,153 1036,0 103,0 Z"/>
+   <glyph unicode="1" horiz-adv-x="874" d="M 156,0 L 156,153 515,153 515,1237 197,1010 197,1180 530,1409 696,1409 696,153 1039,153 1039,0 156,0 Z"/>
+   <glyph unicode="." horiz-adv-x="186" d="M 187,0 L 187,219 382,219 382,0 187,0 Z"/>
+   <glyph unicode="-" horiz-adv-x="504" d="M 91,464 L 91,624 591,624 591,464 91,464 Z"/>
+   <glyph unicode=" " horiz-adv-x="556"/>
+  </font>
+ </defs>
+ <defs class="TextShapeIndex">
+  <g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45 id46 id47 id48 id49 id50 id51 id52"/>
+ </defs>
+ <defs class="EmbeddedBulletChars">
+  <g id="bullet-char-template(57356)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
+  </g>
+  <g id="bullet-char-template(57354)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
+  </g>
+  <g id="bullet-char-template(10146)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
+  </g>
+  <g id="bullet-char-template(10132)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
+  </g>
+  <g id="bullet-char-template(10007)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
+  </g>
+  <g id="bullet-char-template(10004)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
+  </g>
+  <g id="bullet-char-template(9679)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
+  </g>
+  <g id="bullet-char-template(8226)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
+  </g>
+  <g id="bullet-char-template(8211)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
+  </g>
+ </defs>
+ <defs class="TextEmbeddedBitmaps"/>
+ <g>
+  <g id="id2" class="Master_Slide">
+   <g id="bg-id2" class="Background"/>
+   <g id="bo-id2" class="BackgroundObjects"/>
+  </g>
+ </g>
+ <g class="SlideGroup">
+  <g>
+   <g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
+    <g class="Page">
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id3">
+       <rect class="BoundingBox" stroke="none" fill="none" x="3069" y="1769" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 4975,3040 L 3070,3040 3070,1770 6880,1770 6880,3040 4975,3040 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 4975,3040 L 3070,3040 3070,1770 6880,1770 6880,3040 4975,3040 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3568" y="2299"/><tspan class="TextPosition" x="3568" y="2299"><tspan fill="rgb(0,0,0)" stroke="none">Specification</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="4253" y="2854"/><tspan class="TextPosition" x="4253" y="2854"><tspan fill="rgb(0,0,0)" stroke="none">.YAML</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id4">
+       <rect class="BoundingBox" stroke="none" fill="none" x="8146" y="1769" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 10052,3040 L 8147,3040 8147,1770 11957,1770 11957,3040 10052,3040 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10052,3040 L 8147,3040 8147,1770 11957,1770 11957,3040 10052,3040 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="8310" y="2299"/><tspan class="TextPosition" x="8310" y="2299"><tspan fill="rgb(0,0,0)" stroke="none">Data to process</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="9599" y="2854"/><tspan class="TextPosition" x="9599" y="2854"><tspan fill="rgb(0,0,0)" stroke="none">.xml</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id5">
+       <rect class="BoundingBox" stroke="none" fill="none" x="13169" y="1769" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 15075,3040 L 13170,3040 13170,1770 16980,1770 16980,3040 15075,3040 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 15075,3040 L 13170,3040 13170,1770 16980,1770 16980,3040 15075,3040 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13568" y="2299"/><tspan class="TextPosition" x="13568" y="2299"><tspan fill="rgb(0,0,0)" stroke="none">Static content</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="14728" y="2854"/><tspan class="TextPosition" x="14728" y="2854"><tspan fill="rgb(0,0,0)" stroke="none">.rst</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id6">
+       <rect class="BoundingBox" stroke="none" fill="none" x="2630" y="4279" width="4703" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 4980,4280 C 6312,4280 7330,4555 7330,4915 7330,5275 6312,5550 4980,5550 3648,5550 2631,5275 2631,4915 2631,4555 3648,4280 4980,4280 Z M 2631,4280 L 2631,4280 Z M 7331,5551 L 7331,5551 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 4980,4280 C 6312,4280 7330,4555 7330,4915 7330,5275 6312,5550 4980,5550 3648,5550 2631,5275 2631,4915 2631,4555 3648,4280 4980,4280 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 2631,4280 L 2631,4280 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 7331,5551 L 7331,5551 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2980" y="5087"/><tspan class="TextPosition" x="2980" y="5087"><tspan fill="rgb(0,0,0)" stroke="none">read_specification</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id7">
+       <rect class="BoundingBox" stroke="none" fill="none" x="7611" y="4280" width="4830" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 10025,4281 C 11393,4281 12438,4556 12438,4916 12438,5276 11393,5551 10025,5551 8657,5551 7612,5276 7612,4916 7612,4556 8657,4281 10025,4281 Z M 7612,4281 L 7612,4281 Z M 12439,5552 L 12439,5552 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10025,4281 C 11393,4281 12438,4556 12438,4916 12438,5276 11393,5551 10025,5551 8657,5551 7612,5276 7612,4916 7612,4556 8657,4281 10025,4281 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 7612,4281 L 7612,4281 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 12439,5552 L 12439,5552 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="8910" y="5088"/><tspan class="TextPosition" x="8910" y="5088"><tspan fill="rgb(0,0,0)" stroke="none">read_data</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id8">
+       <rect class="BoundingBox" stroke="none" fill="none" x="3069" y="6789" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 4975,8060 L 3070,8060 3070,6790 6880,6790 6880,8060 4975,8060 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 4975,8060 L 3070,8060 3070,6790 6880,6790 6880,8060 4975,8060 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3568" y="7597"/><tspan class="TextPosition" x="3568" y="7597"><tspan fill="rgb(0,0,0)" stroke="none">Specification</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id9">
+       <rect class="BoundingBox" stroke="none" fill="none" x="8119" y="6789" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 10025,8060 L 8120,8060 8120,6790 11930,6790 11930,8060 10025,8060 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10025,8060 L 8120,8060 8120,6790 11930,6790 11930,8060 10025,8060 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="8920" y="7597"/><tspan class="TextPosition" x="8920" y="7597"><tspan fill="rgb(0,0,0)" stroke="none">Input data</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id10">
+       <rect class="BoundingBox" stroke="none" fill="none" x="2657" y="10199" width="4703" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 5007,10200 C 6339,10200 7357,10475 7357,10835 7357,11195 6339,11470 5007,11470 3675,11470 2658,11195 2658,10835 2658,10475 3675,10200 5007,10200 Z M 2658,10200 L 2658,10200 Z M 7358,11471 L 7358,11471 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 5007,10200 C 6339,10200 7357,10475 7357,10835 7357,11195 6339,11470 5007,11470 3675,11470 2658,11195 2658,10835 2658,10475 3675,10200 5007,10200 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 2658,10200 L 2658,10200 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 7358,11471 L 7358,11471 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3918" y="11007"/><tspan class="TextPosition" x="3918" y="11007"><tspan fill="rgb(0,0,0)" stroke="none">filter_data</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id11">
+       <rect class="BoundingBox" stroke="none" fill="none" x="7611" y="10199" width="4830" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 10025,10200 C 11393,10200 12438,10475 12438,10835 12438,11195 11393,11470 10025,11470 8657,11470 7612,11195 7612,10835 7612,10475 8657,10200 10025,10200 Z M 7612,10200 L 7612,10200 Z M 12439,11471 L 12439,11471 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10025,10200 C 11393,10200 12438,10475 12438,10835 12438,11195 11393,11470 10025,11470 8657,11470 7612,11195 7612,10835 7612,10475 8657,10200 10025,10200 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 7612,10200 L 7612,10200 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 12439,11471 L 12439,11471 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="8935" y="11007"/><tspan class="TextPosition" x="8935" y="11007"><tspan fill="rgb(0,0,0)" stroke="none">filter_data</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id12">
+       <rect class="BoundingBox" stroke="none" fill="none" x="12817" y="16899" width="4449" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 15040,16900 C 16300,16900 17263,17175 17263,17535 17263,17895 16300,18170 15040,18170 13780,18170 12818,17895 12818,17535 12818,17175 13780,16900 15040,16900 Z M 12818,16900 L 12818,16900 Z M 17264,18171 L 17264,18171 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 15040,16900 C 16300,16900 17263,17175 17263,17535 17263,17895 16300,18170 15040,18170 13780,18170 12818,17895 12818,17535 12818,17175 13780,16900 15040,16900 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 12818,16900 L 12818,16900 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 17264,18171 L 17264,18171 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13482" y="17707"/><tspan class="TextPosition" x="13482" y="17707"><tspan fill="rgb(0,0,0)" stroke="none">generate_files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id13">
+       <rect class="BoundingBox" stroke="none" fill="none" x="3069" y="14409" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 4975,15680 L 3070,15680 3070,14410 6880,14410 6880,15680 4975,15680 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 4975,15680 L 3070,15680 3070,14410 6880,14410 6880,15680 4975,15680 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="4260" y="15217"/><tspan class="TextPosition" x="4260" y="15217"><tspan fill="rgb(0,0,0)" stroke="none">Tables</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id14">
+       <rect class="BoundingBox" stroke="none" fill="none" x="8119" y="14409" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 10025,15680 L 8120,15680 8120,14410 11930,14410 11930,15680 10025,15680 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10025,15680 L 8120,15680 8120,14410 11930,14410 11930,15680 10025,15680 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="9473" y="15217"/><tspan class="TextPosition" x="9473" y="15217"><tspan fill="rgb(0,0,0)" stroke="none">Plots</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id15">
+       <rect class="BoundingBox" stroke="none" fill="none" x="13169" y="19509" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 15075,20780 L 13170,20780 13170,19510 16980,19510 16980,20780 15075,20780 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 15075,20780 L 13170,20780 13170,19510 16980,19510 16980,20780 15075,20780 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="14552" y="20317"/><tspan class="TextPosition" x="14552" y="20317"><tspan fill="rgb(0,0,0)" stroke="none">Files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id16">
+       <rect class="BoundingBox" stroke="none" fill="none" x="7611" y="22019" width="4830" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 10025,22020 C 11393,22020 12438,22295 12438,22655 12438,23015 11393,23290 10025,23290 8657,23290 7612,23015 7612,22655 7612,22295 8657,22020 10025,22020 Z M 7612,22020 L 7612,22020 Z M 12439,23291 L 12439,23291 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10025,22020 C 11393,22020 12438,22295 12438,22655 12438,23015 11393,23290 10025,23290 8657,23290 7612,23015 7612,22655 7612,22295 8657,22020 10025,22020 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 7612,22020 L 7612,22020 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 12439,23291 L 12439,23291 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="8262" y="22827"/><tspan class="TextPosition" x="8262" y="22827"><tspan fill="rgb(0,0,0)" stroke="none">generate_report</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id17">
+       <rect class="BoundingBox" stroke="none" fill="none" x="8119" y="24629" width="3813" height="1273"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 10025,25900 L 8120,25900 8120,24630 11930,24630 11930,25900 10025,25900 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10025,25900 L 8120,25900 8120,24630 11930,24630 11930,25900 10025,25900 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="9280" y="25437"/><tspan class="TextPosition" x="9280" y="25437"><tspan fill="rgb(0,0,0)" stroke="none">Report</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id18">
+       <rect class="BoundingBox" stroke="none" fill="none" x="1303" y="2404" width="6310" height="20387"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3070,2405 C 1171,2405 -1037,22093 7253,22643"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 7612,22655 L 7167,22489 7157,22789 7612,22655 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id19">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4830" y="3039" width="302" height="1242"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4975,3040 C 4975,3970 4979,3554 4981,3845"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 4982,4280 L 5131,3830 4831,3830 4982,4280 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id20">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9879" y="3039" width="302" height="1243"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10052,3040 C 10052,3971 10036,3555 10029,3846"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 10025,4281 L 10179,3832 9879,3830 10025,4281 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id21">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4826" y="5549" width="302" height="1242"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4982,5550 C 4982,6481 4978,6066 4976,6355"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 4975,6790 L 5126,6340 4826,6340 4975,6790 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id22">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9875" y="5550" width="301" height="1241"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10025,5551 L 10025,6360"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 10025,6790 L 10175,6340 9875,6340 10025,6790 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id23">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4857" y="8059" width="302" height="2142"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4975,8060 C 4975,9665 5002,8796 5008,9766"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 5009,10200 L 5158,9750 4858,9750 5009,10200 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id24">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4974" y="8059" width="3418" height="2328"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4975,8060 C 4975,9665 7780,8678 8251,9972"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 8318,10386 L 8390,9917 8095,9968 8318,10386 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id25">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4974" y="8059" width="10214" height="8842"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4975,8060 C 4975,10304 14789,5995 15037,16490"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 15042,16900 L 15186,16448 14886,16452 15042,16900 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id26">
+       <rect class="BoundingBox" stroke="none" fill="none" x="6597" y="8059" width="3430" height="2328"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10025,8060 C 10025,9665 7210,8678 6737,9972"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 6670,10386 L 6894,9968 6598,9917 6670,10386 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id27">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9875" y="8059" width="301" height="2142"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10025,8060 L 10025,9770"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 10025,10200 L 10175,9750 9875,9750 10025,10200 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id28">
+       <rect class="BoundingBox" stroke="none" fill="none" x="10024" y="8059" width="6737" height="9028"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10025,8060 C 10025,9957 16448,5557 16610,16654"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 16613,17086 L 16760,16635 16460,16637 16613,17086 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id29">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4806" y="13570" width="301" height="841"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4945,13571 C 4945,14201 4952,13931 4959,13981"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 4975,14410 L 5106,13954 4806,13967 4975,14410 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id30">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9875" y="13570" width="301" height="841"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10025,13571 L 10025,13980"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 10025,14410 L 10175,13960 9875,13960 10025,14410 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id31">
+       <rect class="BoundingBox" stroke="none" fill="none" x="14920" y="18169" width="302" height="1342"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15042,18170 C 15042,19176 15063,18709 15072,19080"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 15075,19510 L 15221,19059 14921,19061 15075,19510 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id32">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4974" y="15679" width="3485" height="6528"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4975,15680 C 4975,20435 8106,17374 8308,21746"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 8318,22206 L 8458,21753 8158,21760 8318,22206 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id33">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9875" y="15679" width="301" height="6342"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10025,15680 L 10025,21590"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 10025,22020 L 10175,21570 9875,21570 10025,22020 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id34">
+       <rect class="BoundingBox" stroke="none" fill="none" x="11732" y="20779" width="3345" height="1428"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15075,20780 C 15075,21710 12525,21151 11879,21832"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 11732,22206 L 12043,21848 11766,21733 11732,22206 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id35">
+       <rect class="BoundingBox" stroke="none" fill="none" x="12438" y="2404" width="7822" height="20393"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16980,2405 C 20904,2405 23125,22213 12786,22648"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 12438,22655 L 12891,22795 12885,22495 12438,22655 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id36">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9875" y="23289" width="301" height="1342"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10025,23290 L 10025,24200"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 10025,24630 L 10175,24180 9875,24180 10025,24630 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id37">
+       <rect class="BoundingBox" stroke="none" fill="none" x="2658" y="12300" width="4576" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 4945,12301 C 6241,12301 7231,12576 7231,12936 7231,13296 6241,13571 4945,13571 3649,13571 2659,13296 2659,12936 2659,12576 3649,12301 4945,12301 Z M 2659,12301 L 2659,12301 Z M 7232,13572 L 7232,13572 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 4945,12301 C 6241,12301 7231,12576 7231,12936 7231,13296 6241,13571 4945,13571 3649,13571 2659,13296 2659,12936 2659,12576 3649,12301 4945,12301 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 2659,12301 L 2659,12301 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 7232,13572 L 7232,13572 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3167" y="13108"/><tspan class="TextPosition" x="3167" y="13108"><tspan fill="rgb(0,0,0)" stroke="none">generate_tables</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id38">
+       <rect class="BoundingBox" stroke="none" fill="none" x="7611" y="12300" width="4830" height="1274"/>
+       <path fill="rgb(207,231,245)" stroke="none" d="M 10025,12301 C 11393,12301 12438,12576 12438,12936 12438,13296 11393,13571 10025,13571 8657,13571 7612,13296 7612,12936 7612,12576 8657,12301 10025,12301 Z M 7612,12301 L 7612,12301 Z M 12439,13572 L 12439,13572 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10025,12301 C 11393,12301 12438,12576 12438,12936 12438,13296 11393,13571 10025,13571 8657,13571 7612,13296 7612,12936 7612,12576 8657,12301 10025,12301 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 7612,12301 L 7612,12301 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 12439,13572 L 12439,13572 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="8384" y="13108"/><tspan class="TextPosition" x="8384" y="13108"><tspan fill="rgb(0,0,0)" stroke="none">generate_plots</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id39">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4797" y="11452" width="302" height="850"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4977,11871 C 4977,11387 4954,11245 4947,11868"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 4945,12301 L 5097,11852 4797,11850 4945,12301 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id40">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9827" y="11487" width="302" height="803"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9978,11859 C 9978,11436 9977,11298 9977,11857"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 9977,12289 L 10127,11839 9827,11839 9977,12289 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id41">
+       <rect class="BoundingBox" stroke="none" fill="none" x="11929" y="15044" width="1671" height="2043"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11930,15045 C 12957,15045 13350,15582 13451,16658"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 13470,17086 L 13599,16629 13299,16643 13470,17086 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id42">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4974" y="15679" width="7846" height="2006"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4975,15680 C 4975,16917 7443,17500 12382,17534"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 12819,17535 L 12370,17383 12368,17683 12819,17535 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.ConnectorShape">
+      <g id="id43">
+       <rect class="BoundingBox" stroke="none" fill="none" x="15074" y="3039" width="3534" height="14497"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15075,3040 C 15075,3524 16775,3443 17776,6946 18777,10449 19027,16449 17651,17410"/>
+       <path fill="rgb(0,0,0)" stroke="none" d="M 17264,17535 L 17738,17533 17642,17249 17264,17535 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id44">
+       <rect class="BoundingBox" stroke="none" fill="none" x="499" y="499" width="20003" height="6"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 500,503 L 553,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 606,503 L 659,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 712,503 L 765,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 819,503 L 872,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 925,503 L 978,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1031,503 L 1084,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1137,503 L 1190,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1243,503 L 1296,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1349,503 L 1403,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1456,503 L 1509,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1562,503 L 1615,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1668,503 L 1721,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1774,503 L 1827,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1880,503 L 1933,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1987,503 L 2040,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2093,503 L 2146,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2199,503 L 2252,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2305,503 L 2358,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2411,503 L 2464,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2517,503 L 2571,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2624,503 L 2677,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2730,503 L 2783,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2836,503 L 2889,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2942,503 L 2995,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3048,503 L 3101,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3155,503 L 3208,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3261,503 L 3314,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3367,503 L 3420,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3473,503 L 3526,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3579,503 L 3632,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3685,503 L 3739,503"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3792,503 L 3845,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3898,502 L 3951,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4004,502 L 4057,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4110,502 L 4163,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4216,502 L 4269,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4323,502 L 4376,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4429,502 L 4482,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4535,502 L 4588,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4641,502 L 4694,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4747,502 L 4800,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4854,502 L 4907,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4960,502 L 5013,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5066,502 L 5119,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5172,502 L 5225,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5278,502 L 5331,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5384,502 L 5438,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5491,502 L 5544,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5597,502 L 5650,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5703,502 L 5756,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5809,502 L 5862,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5915,502 L 5968,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6022,502 L 6075,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6128,502 L 6181,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6234,502 L 6287,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6340,502 L 6393,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6446,502 L 6499,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6552,502 L 6606,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6659,502 L 6712,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6765,502 L 6818,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6871,502 L 6924,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6977,502 L 7030,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7083,502 L 7136,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7190,502 L 7243,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7296,502 L 7349,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7402,502 L 7455,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7508,502 L 7561,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7614,502 L 7667,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7720,502 L 7774,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7827,502 L 7880,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7933,502 L 7986,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8039,502 L 8092,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8145,502 L 8198,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8251,502 L 8304,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8358,502 L 8411,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8464,502 L 8517,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8570,502 L 8623,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8676,502 L 8729,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8782,502 L 8835,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8888,502 L 8942,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8995,502 L 9048,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9101,502 L 9154,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9207,502 L 9260,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9313,502 L 9366,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9419,502 L 9472,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9526,502 L 9579,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9632,502 L 9685,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9738,502 L 9791,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9844,502 L 9897,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9950,502 L 10003,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10056,502 L 10110,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10163,502 L 10216,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10269,502 L 10322,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10375,502 L 10428,502"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10481,502 L 10534,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10587,501 L 10640,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10694,501 L 10747,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10800,501 L 10853,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10906,501 L 10959,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11012,501 L 11065,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11118,501 L 11171,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11224,501 L 11278,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11331,501 L 11384,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11437,501 L 11490,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11543,501 L 11596,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11649,501 L 11702,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11755,501 L 11808,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11862,501 L 11915,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11968,501 L 12021,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12074,501 L 12127,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12180,501 L 12233,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12286,501 L 12339,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12392,501 L 12446,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12499,501 L 12552,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12605,501 L 12658,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12711,501 L 12764,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12817,501 L 12870,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12923,501 L 12977,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13030,501 L 13083,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13136,501 L 13189,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13242,501 L 13295,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13348,501 L 13401,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13454,501 L 13507,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13561,501 L 13614,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13667,501 L 13720,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13773,501 L 13826,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13879,501 L 13932,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13985,501 L 14038,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14091,501 L 14145,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14198,501 L 14251,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14304,501 L 14357,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14410,501 L 14463,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14516,501 L 14569,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14622,501 L 14675,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14729,501 L 14782,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14835,501 L 14888,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14941,501 L 14994,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15047,501 L 15100,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15153,501 L 15206,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15259,501 L 15313,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15366,501 L 15419,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15472,501 L 15525,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15578,501 L 15631,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15684,501 L 15737,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15790,501 L 15843,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15897,501 L 15950,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16003,501 L 16056,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16109,501 L 16162,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16215,501 L 16268,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16321,501 L 16374,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16427,501 L 16481,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16534,501 L 16587,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16640,501 L 16693,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16746,501 L 16799,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16852,501 L 16905,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16958,501 L 17011,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17065,501 L 17118,501"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17171,500 L 17224,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17277,500 L 17330,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17383,500 L 17436,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17489,500 L 17542,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17595,500 L 17649,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17702,500 L 17755,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17808,500 L 17861,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17914,500 L 17967,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18020,500 L 18073,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18126,500 L 18179,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18233,500 L 18286,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18339,500 L 18392,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18445,500 L 18498,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18551,500 L 18604,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18657,500 L 18710,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18763,500 L 18817,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18870,500 L 18923,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18976,500 L 19029,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19082,500 L 19135,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19188,500 L 19241,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19294,500 L 19347,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19401,500 L 19454,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19507,500 L 19560,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19613,500 L 19666,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19719,500 L 19772,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19825,500 L 19878,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19931,500 L 19985,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20038,500 L 20091,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20144,500 L 20197,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20250,500 L 20303,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20356,500 L 20409,500"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20462,500 L 20500,500"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id45">
+       <rect class="BoundingBox" stroke="none" fill="none" x="499" y="3600" width="20003" height="6"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 500,3604 L 553,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 606,3604 L 659,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 712,3604 L 765,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 819,3604 L 872,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 925,3604 L 978,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1031,3604 L 1084,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1137,3604 L 1190,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1243,3604 L 1296,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1349,3604 L 1403,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1456,3604 L 1509,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1562,3604 L 1615,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1668,3604 L 1721,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1774,3604 L 1827,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1880,3604 L 1933,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1987,3604 L 2040,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2093,3604 L 2146,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2199,3604 L 2252,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2305,3604 L 2358,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2411,3604 L 2464,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2517,3604 L 2571,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2624,3604 L 2677,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2730,3604 L 2783,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2836,3604 L 2889,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2942,3604 L 2995,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3048,3604 L 3101,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3155,3604 L 3208,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3261,3604 L 3314,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3367,3604 L 3420,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3473,3604 L 3526,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3579,3604 L 3632,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3685,3604 L 3739,3604"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3792,3604 L 3845,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3898,3603 L 3951,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4004,3603 L 4057,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4110,3603 L 4163,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4216,3603 L 4269,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4323,3603 L 4376,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4429,3603 L 4482,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4535,3603 L 4588,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4641,3603 L 4694,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4747,3603 L 4800,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4854,3603 L 4907,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4960,3603 L 5013,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5066,3603 L 5119,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5172,3603 L 5225,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5278,3603 L 5331,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5384,3603 L 5438,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5491,3603 L 5544,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5597,3603 L 5650,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5703,3603 L 5756,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5809,3603 L 5862,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5915,3603 L 5968,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6022,3603 L 6075,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6128,3603 L 6181,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6234,3603 L 6287,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6340,3603 L 6393,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6446,3603 L 6499,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6552,3603 L 6606,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6659,3603 L 6712,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6765,3603 L 6818,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6871,3603 L 6924,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6977,3603 L 7030,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7083,3603 L 7136,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7190,3603 L 7243,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7296,3603 L 7349,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7402,3603 L 7455,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7508,3603 L 7561,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7614,3603 L 7667,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7720,3603 L 7774,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7827,3603 L 7880,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7933,3603 L 7986,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8039,3603 L 8092,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8145,3603 L 8198,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8251,3603 L 8304,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8358,3603 L 8411,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8464,3603 L 8517,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8570,3603 L 8623,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8676,3603 L 8729,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8782,3603 L 8835,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8888,3603 L 8942,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8995,3603 L 9048,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9101,3603 L 9154,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9207,3603 L 9260,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9313,3603 L 9366,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9419,3603 L 9472,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9526,3603 L 9579,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9632,3603 L 9685,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9738,3603 L 9791,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9844,3603 L 9897,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9950,3603 L 10003,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10056,3603 L 10110,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10163,3603 L 10216,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10269,3603 L 10322,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10375,3603 L 10428,3603"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10481,3603 L 10534,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10587,3602 L 10640,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10694,3602 L 10747,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10800,3602 L 10853,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10906,3602 L 10959,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11012,3602 L 11065,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11118,3602 L 11171,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11224,3602 L 11278,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11331,3602 L 11384,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11437,3602 L 11490,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11543,3602 L 11596,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11649,3602 L 11702,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11755,3602 L 11808,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11862,3602 L 11915,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11968,3602 L 12021,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12074,3602 L 12127,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12180,3602 L 12233,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12286,3602 L 12339,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12392,3602 L 12446,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12499,3602 L 12552,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12605,3602 L 12658,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12711,3602 L 12764,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12817,3602 L 12870,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12923,3602 L 12977,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13030,3602 L 13083,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13136,3602 L 13189,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13242,3602 L 13295,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13348,3602 L 13401,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13454,3602 L 13507,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13561,3602 L 13614,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13667,3602 L 13720,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13773,3602 L 13826,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13879,3602 L 13932,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13985,3602 L 14038,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14091,3602 L 14145,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14198,3602 L 14251,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14304,3602 L 14357,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14410,3602 L 14463,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14516,3602 L 14569,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14622,3602 L 14675,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14729,3602 L 14782,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14835,3602 L 14888,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14941,3602 L 14994,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15047,3602 L 15100,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15153,3602 L 15206,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15259,3602 L 15313,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15366,3602 L 15419,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15472,3602 L 15525,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15578,3602 L 15631,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15684,3602 L 15737,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15790,3602 L 15843,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15897,3602 L 15950,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16003,3602 L 16056,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16109,3602 L 16162,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16215,3602 L 16268,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16321,3602 L 16374,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16427,3602 L 16481,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16534,3602 L 16587,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16640,3602 L 16693,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16746,3602 L 16799,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16852,3602 L 16905,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16958,3602 L 17011,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17065,3602 L 17118,3602"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17171,3601 L 17224,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17277,3601 L 17330,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17383,3601 L 17436,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17489,3601 L 17542,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17595,3601 L 17649,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17702,3601 L 17755,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17808,3601 L 17861,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17914,3601 L 17967,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18020,3601 L 18073,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18126,3601 L 18179,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18233,3601 L 18286,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18339,3601 L 18392,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18445,3601 L 18498,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18551,3601 L 18604,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18657,3601 L 18710,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18763,3601 L 18817,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18870,3601 L 18923,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18976,3601 L 19029,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19082,3601 L 19135,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19188,3601 L 19241,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19294,3601 L 19347,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19401,3601 L 19454,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19507,3601 L 19560,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19613,3601 L 19666,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19719,3601 L 19772,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19825,3601 L 19878,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19931,3601 L 19985,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20038,3601 L 20091,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20144,3601 L 20197,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20250,3601 L 20303,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20356,3601 L 20409,3601"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20462,3601 L 20500,3601"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id46">
+       <rect class="BoundingBox" stroke="none" fill="none" x="499" y="11701" width="20003" height="6"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 500,11705 L 553,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 606,11705 L 659,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 712,11705 L 765,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 819,11705 L 872,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 925,11705 L 978,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1031,11705 L 1084,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1137,11705 L 1190,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1243,11705 L 1296,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1349,11705 L 1403,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1456,11705 L 1509,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1562,11705 L 1615,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1668,11705 L 1721,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1774,11705 L 1827,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1880,11705 L 1933,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1987,11705 L 2040,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2093,11705 L 2146,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2199,11705 L 2252,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2305,11705 L 2358,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2411,11705 L 2464,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2517,11705 L 2571,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2624,11705 L 2677,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2730,11705 L 2783,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2836,11705 L 2889,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2942,11705 L 2995,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3048,11705 L 3101,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3155,11705 L 3208,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3261,11705 L 3314,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3367,11705 L 3420,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3473,11705 L 3526,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3579,11705 L 3632,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3685,11705 L 3739,11705"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3792,11705 L 3845,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3898,11704 L 3951,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4004,11704 L 4057,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4110,11704 L 4163,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4216,11704 L 4269,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4323,11704 L 4376,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4429,11704 L 4482,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4535,11704 L 4588,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4641,11704 L 4694,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4747,11704 L 4800,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4854,11704 L 4907,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4960,11704 L 5013,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5066,11704 L 5119,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5172,11704 L 5225,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5278,11704 L 5331,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5384,11704 L 5438,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5491,11704 L 5544,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5597,11704 L 5650,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5703,11704 L 5756,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5809,11704 L 5862,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5915,11704 L 5968,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6022,11704 L 6075,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6128,11704 L 6181,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6234,11704 L 6287,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6340,11704 L 6393,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6446,11704 L 6499,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6552,11704 L 6606,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6659,11704 L 6712,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6765,11704 L 6818,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6871,11704 L 6924,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6977,11704 L 7030,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7083,11704 L 7136,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7190,11704 L 7243,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7296,11704 L 7349,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7402,11704 L 7455,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7508,11704 L 7561,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7614,11704 L 7667,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7720,11704 L 7774,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7827,11704 L 7880,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7933,11704 L 7986,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8039,11704 L 8092,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8145,11704 L 8198,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8251,11704 L 8304,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8358,11704 L 8411,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8464,11704 L 8517,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8570,11704 L 8623,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8676,11704 L 8729,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8782,11704 L 8835,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8888,11704 L 8942,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8995,11704 L 9048,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9101,11704 L 9154,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9207,11704 L 9260,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9313,11704 L 9366,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9419,11704 L 9472,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9526,11704 L 9579,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9632,11704 L 9685,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9738,11704 L 9791,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9844,11704 L 9897,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9950,11704 L 10003,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10056,11704 L 10110,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10163,11704 L 10216,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10269,11704 L 10322,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10375,11704 L 10428,11704"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10481,11704 L 10534,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10587,11703 L 10640,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10694,11703 L 10747,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10800,11703 L 10853,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10906,11703 L 10959,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11012,11703 L 11065,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11118,11703 L 11171,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11224,11703 L 11278,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11331,11703 L 11384,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11437,11703 L 11490,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11543,11703 L 11596,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11649,11703 L 11702,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11755,11703 L 11808,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11862,11703 L 11915,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11968,11703 L 12021,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12074,11703 L 12127,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12180,11703 L 12233,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12286,11703 L 12339,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12392,11703 L 12446,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12499,11703 L 12552,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12605,11703 L 12658,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12711,11703 L 12764,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12817,11703 L 12870,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12923,11703 L 12977,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13030,11703 L 13083,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13136,11703 L 13189,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13242,11703 L 13295,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13348,11703 L 13401,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13454,11703 L 13507,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13561,11703 L 13614,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13667,11703 L 13720,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13773,11703 L 13826,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13879,11703 L 13932,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13985,11703 L 14038,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14091,11703 L 14145,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14198,11703 L 14251,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14304,11703 L 14357,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14410,11703 L 14463,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14516,11703 L 14569,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14622,11703 L 14675,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14729,11703 L 14782,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14835,11703 L 14888,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14941,11703 L 14994,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15047,11703 L 15100,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15153,11703 L 15206,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15259,11703 L 15313,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15366,11703 L 15419,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15472,11703 L 15525,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15578,11703 L 15631,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15684,11703 L 15737,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15790,11703 L 15843,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15897,11703 L 15950,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16003,11703 L 16056,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16109,11703 L 16162,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16215,11703 L 16268,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16321,11703 L 16374,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16427,11703 L 16481,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16534,11703 L 16587,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16640,11703 L 16693,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16746,11703 L 16799,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16852,11703 L 16905,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16958,11703 L 17011,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17065,11703 L 17118,11703"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17171,11702 L 17224,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17277,11702 L 17330,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17383,11702 L 17436,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17489,11702 L 17542,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17595,11702 L 17649,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17702,11702 L 17755,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17808,11702 L 17861,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17914,11702 L 17967,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18020,11702 L 18073,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18126,11702 L 18179,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18233,11702 L 18286,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18339,11702 L 18392,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18445,11702 L 18498,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18551,11702 L 18604,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18657,11702 L 18710,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18763,11702 L 18817,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18870,11702 L 18923,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18976,11702 L 19029,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19082,11702 L 19135,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19188,11702 L 19241,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19294,11702 L 19347,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19401,11702 L 19454,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19507,11702 L 19560,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19613,11702 L 19666,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19719,11702 L 19772,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19825,11702 L 19878,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19931,11702 L 19985,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20038,11702 L 20091,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20144,11702 L 20197,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20250,11702 L 20303,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20356,11702 L 20409,11702"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20462,11702 L 20500,11702"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id47">
+       <rect class="BoundingBox" stroke="none" fill="none" x="499" y="21402" width="20003" height="6"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 500,21406 L 553,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 606,21406 L 659,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 712,21406 L 765,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 819,21406 L 872,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 925,21406 L 978,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1031,21406 L 1084,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1137,21406 L 1190,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1243,21406 L 1296,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1349,21406 L 1403,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1456,21406 L 1509,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1562,21406 L 1615,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1668,21406 L 1721,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1774,21406 L 1827,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1880,21406 L 1933,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1987,21406 L 2040,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2093,21406 L 2146,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2199,21406 L 2252,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2305,21406 L 2358,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2411,21406 L 2464,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2517,21406 L 2571,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2624,21406 L 2677,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2730,21406 L 2783,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2836,21406 L 2889,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2942,21406 L 2995,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3048,21406 L 3101,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3155,21406 L 3208,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3261,21406 L 3314,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3367,21406 L 3420,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3473,21406 L 3526,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3579,21406 L 3632,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3685,21406 L 3739,21406"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3792,21406 L 3845,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3898,21405 L 3951,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4004,21405 L 4057,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4110,21405 L 4163,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4216,21405 L 4269,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4323,21405 L 4376,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4429,21405 L 4482,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4535,21405 L 4588,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4641,21405 L 4694,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4747,21405 L 4800,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4854,21405 L 4907,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4960,21405 L 5013,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5066,21405 L 5119,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5172,21405 L 5225,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5278,21405 L 5331,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5384,21405 L 5438,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5491,21405 L 5544,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5597,21405 L 5650,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5703,21405 L 5756,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5809,21405 L 5862,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5915,21405 L 5968,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6022,21405 L 6075,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6128,21405 L 6181,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6234,21405 L 6287,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6340,21405 L 6393,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6446,21405 L 6499,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6552,21405 L 6606,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6659,21405 L 6712,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6765,21405 L 6818,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6871,21405 L 6924,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6977,21405 L 7030,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7083,21405 L 7136,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7190,21405 L 7243,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7296,21405 L 7349,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7402,21405 L 7455,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7508,21405 L 7561,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7614,21405 L 7667,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7720,21405 L 7774,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7827,21405 L 7880,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7933,21405 L 7986,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8039,21405 L 8092,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8145,21405 L 8198,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8251,21405 L 8304,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8358,21405 L 8411,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8464,21405 L 8517,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8570,21405 L 8623,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8676,21405 L 8729,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8782,21405 L 8835,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8888,21405 L 8942,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8995,21405 L 9048,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9101,21405 L 9154,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9207,21405 L 9260,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9313,21405 L 9366,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9419,21405 L 9472,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9526,21405 L 9579,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9632,21405 L 9685,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9738,21405 L 9791,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9844,21405 L 9897,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9950,21405 L 10003,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10056,21405 L 10110,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10163,21405 L 10216,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10269,21405 L 10322,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10375,21405 L 10428,21405"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10481,21405 L 10534,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10587,21404 L 10640,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10694,21404 L 10747,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10800,21404 L 10853,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10906,21404 L 10959,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11012,21404 L 11065,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11118,21404 L 11171,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11224,21404 L 11278,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11331,21404 L 11384,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11437,21404 L 11490,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11543,21404 L 11596,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11649,21404 L 11702,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11755,21404 L 11808,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11862,21404 L 11915,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11968,21404 L 12021,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12074,21404 L 12127,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12180,21404 L 12233,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12286,21404 L 12339,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12392,21404 L 12446,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12499,21404 L 12552,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12605,21404 L 12658,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12711,21404 L 12764,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12817,21404 L 12870,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12923,21404 L 12977,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13030,21404 L 13083,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13136,21404 L 13189,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13242,21404 L 13295,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13348,21404 L 13401,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13454,21404 L 13507,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13561,21404 L 13614,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13667,21404 L 13720,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13773,21404 L 13826,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13879,21404 L 13932,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13985,21404 L 14038,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14091,21404 L 14145,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14198,21404 L 14251,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14304,21404 L 14357,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14410,21404 L 14463,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14516,21404 L 14569,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14622,21404 L 14675,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14729,21404 L 14782,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14835,21404 L 14888,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14941,21404 L 14994,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15047,21404 L 15100,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15153,21404 L 15206,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15259,21404 L 15313,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15366,21404 L 15419,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15472,21404 L 15525,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15578,21404 L 15631,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15684,21404 L 15737,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15790,21404 L 15843,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15897,21404 L 15950,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16003,21404 L 16056,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16109,21404 L 16162,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16215,21404 L 16268,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16321,21404 L 16374,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16427,21404 L 16481,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16534,21404 L 16587,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16640,21404 L 16693,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16746,21404 L 16799,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16852,21404 L 16905,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16958,21404 L 17011,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17065,21404 L 17118,21404"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17171,21403 L 17224,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17277,21403 L 17330,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17383,21403 L 17436,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17489,21403 L 17542,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17595,21403 L 17649,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17702,21403 L 17755,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17808,21403 L 17861,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17914,21403 L 17967,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18020,21403 L 18073,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18126,21403 L 18179,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18233,21403 L 18286,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18339,21403 L 18392,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18445,21403 L 18498,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18551,21403 L 18604,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18657,21403 L 18710,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18763,21403 L 18817,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18870,21403 L 18923,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18976,21403 L 19029,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19082,21403 L 19135,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19188,21403 L 19241,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19294,21403 L 19347,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19401,21403 L 19454,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19507,21403 L 19560,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19613,21403 L 19666,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19719,21403 L 19772,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19825,21403 L 19878,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19931,21403 L 19985,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20038,21403 L 20091,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20144,21403 L 20197,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20250,21403 L 20303,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20356,21403 L 20409,21403"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20462,21403 L 20500,21403"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id48">
+       <rect class="BoundingBox" stroke="none" fill="none" x="499" y="26503" width="20003" height="6"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 500,26507 L 553,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 606,26507 L 659,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 712,26507 L 765,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 819,26507 L 872,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 925,26507 L 978,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1031,26507 L 1084,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1137,26507 L 1190,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1243,26507 L 1296,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1349,26507 L 1403,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1456,26507 L 1509,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1562,26507 L 1615,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1668,26507 L 1721,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1774,26507 L 1827,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1880,26507 L 1933,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 1987,26507 L 2040,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2093,26507 L 2146,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2199,26507 L 2252,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2305,26507 L 2358,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2411,26507 L 2464,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2517,26507 L 2571,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2624,26507 L 2677,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2730,26507 L 2783,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2836,26507 L 2889,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 2942,26507 L 2995,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3048,26507 L 3101,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3155,26507 L 3208,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3261,26507 L 3314,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3367,26507 L 3420,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3473,26507 L 3526,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3579,26507 L 3632,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3685,26507 L 3739,26507"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3792,26507 L 3845,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 3898,26506 L 3951,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4004,26506 L 4057,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4110,26506 L 4163,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4216,26506 L 4269,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4323,26506 L 4376,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4429,26506 L 4482,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4535,26506 L 4588,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4641,26506 L 4694,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4747,26506 L 4800,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4854,26506 L 4907,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 4960,26506 L 5013,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5066,26506 L 5119,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5172,26506 L 5225,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5278,26506 L 5331,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5384,26506 L 5438,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5491,26506 L 5544,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5597,26506 L 5650,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5703,26506 L 5756,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5809,26506 L 5862,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 5915,26506 L 5968,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6022,26506 L 6075,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6128,26506 L 6181,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6234,26506 L 6287,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6340,26506 L 6393,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6446,26506 L 6499,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6552,26506 L 6606,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6659,26506 L 6712,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6765,26506 L 6818,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6871,26506 L 6924,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 6977,26506 L 7030,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7083,26506 L 7136,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7190,26506 L 7243,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7296,26506 L 7349,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7402,26506 L 7455,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7508,26506 L 7561,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7614,26506 L 7667,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7720,26506 L 7774,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7827,26506 L 7880,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 7933,26506 L 7986,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8039,26506 L 8092,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8145,26506 L 8198,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8251,26506 L 8304,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8358,26506 L 8411,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8464,26506 L 8517,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8570,26506 L 8623,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8676,26506 L 8729,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8782,26506 L 8835,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8888,26506 L 8942,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 8995,26506 L 9048,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9101,26506 L 9154,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9207,26506 L 9260,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9313,26506 L 9366,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9419,26506 L 9472,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9526,26506 L 9579,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9632,26506 L 9685,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9738,26506 L 9791,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9844,26506 L 9897,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 9950,26506 L 10003,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10056,26506 L 10110,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10163,26506 L 10216,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10269,26506 L 10322,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10375,26506 L 10428,26506"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10481,26506 L 10534,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10587,26505 L 10640,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10694,26505 L 10747,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10800,26505 L 10853,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 10906,26505 L 10959,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11012,26505 L 11065,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11118,26505 L 11171,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11224,26505 L 11278,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11331,26505 L 11384,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11437,26505 L 11490,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11543,26505 L 11596,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11649,26505 L 11702,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11755,26505 L 11808,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11862,26505 L 11915,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 11968,26505 L 12021,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12074,26505 L 12127,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12180,26505 L 12233,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12286,26505 L 12339,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12392,26505 L 12446,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12499,26505 L 12552,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12605,26505 L 12658,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12711,26505 L 12764,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12817,26505 L 12870,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 12923,26505 L 12977,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13030,26505 L 13083,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13136,26505 L 13189,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13242,26505 L 13295,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13348,26505 L 13401,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13454,26505 L 13507,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13561,26505 L 13614,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13667,26505 L 13720,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13773,26505 L 13826,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13879,26505 L 13932,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 13985,26505 L 14038,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14091,26505 L 14145,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14198,26505 L 14251,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14304,26505 L 14357,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14410,26505 L 14463,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14516,26505 L 14569,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14622,26505 L 14675,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14729,26505 L 14782,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14835,26505 L 14888,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 14941,26505 L 14994,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15047,26505 L 15100,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15153,26505 L 15206,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15259,26505 L 15313,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15366,26505 L 15419,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15472,26505 L 15525,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15578,26505 L 15631,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15684,26505 L 15737,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15790,26505 L 15843,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 15897,26505 L 15950,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16003,26505 L 16056,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16109,26505 L 16162,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16215,26505 L 16268,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16321,26505 L 16374,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16427,26505 L 16481,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16534,26505 L 16587,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16640,26505 L 16693,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16746,26505 L 16799,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16852,26505 L 16905,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 16958,26505 L 17011,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17065,26505 L 17118,26505"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17171,26504 L 17224,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17277,26504 L 17330,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17383,26504 L 17436,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17489,26504 L 17542,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17595,26504 L 17649,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17702,26504 L 17755,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17808,26504 L 17861,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 17914,26504 L 17967,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18020,26504 L 18073,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18126,26504 L 18179,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18233,26504 L 18286,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18339,26504 L 18392,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18445,26504 L 18498,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18551,26504 L 18604,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18657,26504 L 18710,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18763,26504 L 18817,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18870,26504 L 18923,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 18976,26504 L 19029,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19082,26504 L 19135,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19188,26504 L 19241,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19294,26504 L 19347,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19401,26504 L 19454,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19507,26504 L 19560,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19613,26504 L 19666,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19719,26504 L 19772,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19825,26504 L 19878,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 19931,26504 L 19985,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20038,26504 L 20091,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20144,26504 L 20197,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20250,26504 L 20303,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20356,26504 L 20409,26504"/>
+       <path fill="none" stroke="rgb(0,0,0)" d="M 20462,26504 L 20500,26504"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id49">
+       <rect class="BoundingBox" stroke="none" fill="none" x="500" y="554" width="5081" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="750" y="1128"/><tspan class="TextPosition" x="750" y="1128"><tspan fill="rgb(0,0,0)" stroke="none">sL1 - Data</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id50">
+       <rect class="BoundingBox" stroke="none" fill="none" x="500" y="8554" width="4827" height="1362"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="750" y="9128"/><tspan class="TextPosition" x="750" y="9128"><tspan fill="rgb(0,0,0)" stroke="none">sL2 - Data </tspan></tspan><tspan class="TextPosition" x="750" y="9683"/><tspan class="TextPosition" x="750" y="9683"><tspan fill="rgb(0,0,0)" stroke="none">processing</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id51">
+       <rect class="BoundingBox" stroke="none" fill="none" x="500" y="19754" width="5081" height="1362"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="750" y="20328"/><tspan class="TextPosition" x="750" y="20328"><tspan fill="rgb(0,0,0)" stroke="none">sL3 - Data </tspan></tspan><tspan class="TextPosition" x="750" y="20883"/><tspan class="TextPosition" x="750" y="20883"><tspan fill="rgb(0,0,0)" stroke="none">presentation</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id52">
+       <rect class="BoundingBox" stroke="none" fill="none" x="500" y="24554" width="5081" height="1362"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="750" y="25128"/><tspan class="TextPosition" x="750" y="25128"><tspan fill="rgb(0,0,0)" stroke="none">sL4 - Report </tspan></tspan><tspan class="TextPosition" x="750" y="25683"/><tspan class="TextPosition" x="750" y="25683"><tspan fill="rgb(0,0,0)" stroke="none">generation</tspan></tspan></tspan></text>
+      </g>
+     </g>
+    </g>
+   </g>
+  </g>
+ </g>
+</svg>
\ No newline at end of file
diff --git a/resources/tools/presentation/new/doc/pal_layers.svg b/resources/tools/presentation/new/doc/pal_layers.svg
new file mode 100644 (file)
index 0000000..dfb05d3
--- /dev/null
@@ -0,0 +1,441 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<svg version="1.2" baseProfile="tiny" width="215.9mm" height="152.4mm" viewBox="0 0 21590 15240" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
+ <defs class="ClipPathGroup">
+  <clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
+   <rect x="0" y="0" width="21590" height="15240"/>
+  </clipPath>
+ </defs>
+ <defs>
+  <font id="EmbeddedFont_1" horiz-adv-x="2048">
+   <font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
+   <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
+   <glyph unicode="→" horiz-adv-x="1218" d="M 1407,637 C 1388,660 1368,687 1349,720 1329,753 1309,791 1288,834 L 1360,834 C 1399,789 1440,747 1483,710 1526,673 1575,640 1630,612 L 1630,576 C 1575,548 1526,515 1483,478 1440,441 1399,399 1360,354 L 1288,354 C 1309,397 1329,435 1349,468 1368,501 1388,528 1407,551 L 418,551 418,637 1407,637 Z"/>
+   <glyph unicode="y" horiz-adv-x="1059" d="M 604,1 C 579,-64 553,-123 527,-175 500,-227 471,-272 438,-309 405,-346 369,-374 329,-394 289,-413 243,-423 191,-423 168,-423 147,-423 128,-423 109,-423 88,-420 67,-414 L 67,-279 C 80,-282 94,-284 110,-284 126,-284 140,-284 151,-284 204,-284 253,-264 298,-225 343,-186 383,-123 417,-37 L 434,6 5,1082 197,1082 425,484 C 432,466 440,442 451,412 461,382 471,352 482,322 492,292 501,265 509,241 517,217 522,202 523,196 525,203 530,218 538,240 545,261 554,285 564,312 573,339 583,366 593,393 603,420 611,444 618,464 L 830,1082 1020,1082 604,1 Z"/>
+   <glyph unicode="x" horiz-adv-x="1033" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
+   <glyph unicode="v" horiz-adv-x="1059" d="M 613,0 L 400,0 7,1082 199,1082 437,378 C 442,363 447,346 454,325 460,304 466,282 473,259 480,236 486,215 492,194 497,173 502,155 506,141 510,155 515,173 522,194 528,215 534,236 541,258 548,280 555,302 562,323 569,344 575,361 580,376 L 826,1082 1017,1082 613,0 Z"/>
+   <glyph unicode="u" horiz-adv-x="901" d="M 314,1082 L 314,396 C 314,343 318,299 326,264 333,229 346,200 363,179 380,157 403,142 432,133 460,124 495,119 537,119 580,119 618,127 653,142 687,157 716,178 741,207 765,235 784,270 797,312 810,353 817,401 817,455 L 817,1082 997,1082 997,231 C 997,208 997,185 998,160 998,135 998,111 999,89 1000,66 1000,47 1001,31 1002,15 1002,5 1003,0 L 833,0 C 832,3 832,12 831,27 830,42 830,59 829,78 828,97 827,116 826,136 825,155 825,172 825,185 L 822,185 C 805,154 786,125 765,100 744,75 720,53 693,36 666,18 634,4 599,-6 564,-15 523,-20 476,-20 416,-20 364,-13 321,2 278,17 242,39 214,70 186,101 166,140 153,188 140,236 133,294 133,361 L 133,1082 314,1082 Z"/>
+   <glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 527,1 499,-5 471,-10 442,-14 409,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 467,127 484,128 501,131 517,134 535,137 554,141 L 554,8 Z"/>
+   <glyph unicode="s" horiz-adv-x="927" d="M 950,299 C 950,248 940,203 921,164 901,124 872,91 835,64 798,37 752,16 698,2 643,-13 581,-20 511,-20 448,-20 392,-15 342,-6 291,4 247,20 209,41 171,62 139,91 114,126 88,161 69,203 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 550,117 585,120 618,125 650,130 678,140 701,153 724,166 743,183 756,205 769,226 775,253 775,285 775,318 767,345 752,366 737,387 715,404 688,418 661,432 628,444 589,455 550,465 507,476 460,489 417,500 374,513 331,527 288,541 250,560 216,583 181,606 153,634 132,668 111,702 100,745 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 763,842 752,866 736,885 720,904 701,919 678,931 655,942 630,951 602,956 573,961 544,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,785 282,761 297,742 311,723 331,707 357,694 382,681 413,669 449,660 485,650 525,640 568,629 597,622 626,614 656,606 686,597 715,587 744,576 772,564 799,550 824,535 849,519 870,500 889,478 908,456 923,430 934,401 945,372 950,338 950,299 Z"/>
+   <glyph unicode="r" horiz-adv-x="556" d="M 142,0 L 142,830 C 142,853 142,876 142,900 141,923 141,946 140,968 139,990 139,1011 138,1030 137,1049 137,1067 136,1082 L 306,1082 C 307,1067 308,1049 309,1030 310,1010 311,990 312,969 313,948 313,929 314,910 314,891 314,874 314,861 L 318,861 C 331,902 344,938 359,969 373,999 390,1024 409,1044 428,1063 451,1078 478,1088 505,1097 537,1102 575,1102 590,1102 604,1101 617,1099 630,1096 641,1094 648,1092 L 648,927 C 636,930 622,933 606,935 590,936 572,937 552,937 511,937 476,928 447,909 418,890 394,865 376,832 357,799 344,759 335,714 326,668 322,618 322,564 L 322,0 142,0 Z"/>
+   <glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,464 1046,388 1033,319 1020,250 998,190 967,140 936,90 895,51 844,23 793,-6 730,-20 655,-20 578,-20 510,-5 452,24 394,53 350,101 319,168 L 314,168 C 315,167 315,161 316,150 316,139 316,126 317,110 317,94 317,76 318,57 318,37 318,17 318,-2 L 318,-425 138,-425 138,861 C 138,887 138,912 138,936 137,960 137,982 136,1002 135,1021 135,1038 134,1052 133,1066 133,1076 132,1082 L 306,1082 C 307,1080 308,1073 309,1061 310,1049 311,1035 312,1018 313,1001 314,982 315,963 316,944 316,925 316,908 L 320,908 C 337,943 356,972 377,997 398,1021 423,1041 450,1057 477,1072 508,1084 542,1091 575,1098 613,1101 655,1101 730,1101 793,1088 844,1061 895,1034 936,997 967,949 998,900 1020,842 1033,774 1046,705 1053,629 1053,546 Z M 864,542 C 864,609 860,668 852,720 844,772 830,816 811,852 791,888 765,915 732,934 699,953 658,962 609,962 569,962 531,956 496,945 461,934 430,912 404,880 377,848 356,804 341,748 326,691 318,618 318,528 318,451 324,387 337,334 350,281 368,238 393,205 417,172 447,149 483,135 519,120 560,113 607,113 657,113 699,123 732,142 765,161 791,189 811,226 830,263 844,308 852,361 860,414 864,474 864,542 Z"/>
+   <glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 490,-20 422,-9 363,14 304,37 254,71 213,118 172,165 140,223 119,294 97,364 86,447 86,542 86,915 248,1102 571,1102 655,1102 728,1090 789,1067 850,1044 900,1009 939,962 978,915 1006,857 1025,787 1044,717 1053,635 1053,542 Z M 864,542 C 864,626 858,695 845,750 832,805 813,848 788,881 763,914 732,937 696,950 660,963 619,969 574,969 528,969 487,962 450,949 413,935 381,912 355,879 329,846 309,802 296,747 282,692 275,624 275,542 275,458 282,389 297,334 312,279 332,235 358,202 383,169 414,146 449,133 484,120 522,113 563,113 609,113 651,120 688,133 725,146 757,168 783,201 809,234 829,278 843,333 857,388 864,458 864,542 Z"/>
+   <glyph unicode="n" horiz-adv-x="900" d="M 825,0 L 825,686 C 825,739 821,783 814,818 806,853 793,882 776,904 759,925 736,941 708,950 679,959 644,963 602,963 559,963 521,956 487,941 452,926 423,904 399,876 374,847 355,812 342,771 329,729 322,681 322,627 L 322,0 142,0 142,851 C 142,874 142,898 142,923 141,948 141,971 140,994 139,1016 139,1035 138,1051 137,1067 137,1077 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 312,966 313,947 314,927 314,910 314,897 L 317,897 C 334,928 353,957 374,982 395,1007 419,1029 446,1047 473,1064 505,1078 540,1088 575,1097 616,1102 663,1102 723,1102 775,1095 818,1080 861,1065 897,1043 925,1012 953,981 974,942 987,894 1000,845 1006,788 1006,721 L 1006,0 825,0 Z"/>
+   <glyph unicode="m" horiz-adv-x="1456" d="M 768,0 L 768,686 C 768,739 765,783 758,818 751,853 740,882 725,904 709,925 688,941 663,950 638,959 607,963 570,963 532,963 498,956 467,941 436,926 410,904 389,876 367,847 350,812 339,771 327,729 321,681 321,627 L 321,0 142,0 142,851 C 142,874 142,898 142,923 141,948 141,971 140,994 139,1016 139,1035 138,1051 137,1067 137,1077 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 312,966 313,947 314,927 314,910 314,897 L 317,897 C 333,928 350,957 369,982 388,1007 410,1029 435,1047 460,1064 488,1078 521,1088 553,1097 590,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 946,928 964,957 984,982 1004,1007 1027,1029 1054,1047 1081,1064 1111,1078 1144,1088 1177,1097 1215,1102 1258,1102 1313,1102 1360,1095 1400,1080 1439,1065 1472,1043 1497,1012 1522,981 1541,942 1553,894 1565,845 1571,788 1571,721 L 1571,0 1393,0 1393,686 C 1393,739 1390,783 1383,818 1376,853 1365,882 1350,904 1334,925 1313,941 1288,950 1263,959 1232,963 1195,963 1157,963 1123,956 1092,942 1061,927 1035,906 1014,878 992,850 975,815 964,773 952,731 946,682 946,627 L 946,0 768,0 Z"/>
+   <glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
+   <glyph unicode="k" horiz-adv-x="927" d="M 816,0 L 450,494 318,385 318,0 138,0 138,1484 318,1484 318,557 793,1082 1004,1082 565,617 1027,0 816,0 Z"/>
+   <glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
+   <glyph unicode="h" horiz-adv-x="874" d="M 317,897 C 337,934 359,965 382,991 405,1016 431,1037 459,1054 487,1071 518,1083 551,1091 584,1098 622,1102 663,1102 732,1102 789,1093 834,1074 878,1055 913,1029 939,996 964,962 982,922 992,875 1001,828 1006,777 1006,721 L 1006,0 825,0 825,686 C 825,732 822,772 817,807 811,842 800,871 784,894 768,917 745,934 716,946 687,957 649,963 602,963 559,963 521,955 487,940 452,925 423,903 399,875 374,847 355,813 342,773 329,733 322,688 322,638 L 322,0 142,0 142,1484 322,1484 322,1098 C 322,1076 322,1054 321,1032 320,1010 320,990 319,971 318,952 317,937 316,924 315,911 315,902 314,897 L 317,897 Z"/>
+   <glyph unicode="g" horiz-adv-x="954" d="M 548,-425 C 486,-425 431,-419 383,-406 335,-393 294,-375 260,-352 226,-328 198,-300 177,-267 156,-234 140,-198 131,-158 L 312,-132 C 324,-182 351,-220 392,-248 433,-274 486,-288 553,-288 594,-288 631,-282 664,-271 697,-260 726,-241 749,-217 772,-191 790,-159 803,-119 816,-79 822,-30 822,27 L 822,201 820,201 C 807,174 790,148 771,123 751,98 727,75 699,56 670,37 637,21 600,10 563,-2 520,-8 472,-8 403,-8 345,4 296,27 247,50 207,84 176,130 145,176 122,233 108,302 93,370 86,449 86,539 86,626 93,704 108,773 122,842 145,901 178,950 210,998 252,1035 304,1061 355,1086 418,1099 492,1099 569,1099 635,1082 692,1047 748,1012 791,962 822,897 L 824,897 C 824,914 825,932 826,953 827,974 828,993 829,1012 830,1030 831,1046 832,1059 833,1072 835,1080 836,1082 L 1007,1082 C 1006,1076 1006,1066 1005,1052 1004,1037 1004,1020 1003,1000 1002,980 1002,958 1002,934 1001,909 1001,884 1001,858 L 1001,31 C 1001,-120 964,-234 890,-311 815,-387 701,-425 548,-425 Z M 822,541 C 822,616 814,681 798,735 781,788 760,832 733,866 706,900 676,925 642,941 607,957 572,965 536,965 490,965 451,957 418,941 385,925 357,900 336,866 314,831 298,787 288,734 277,680 272,616 272,541 272,463 277,398 288,345 298,292 314,249 335,216 356,183 383,160 416,146 449,132 488,125 533,125 569,125 604,133 639,148 673,163 704,188 731,221 758,254 780,297 797,350 814,403 822,466 822,541 Z"/>
+   <glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1243 185,1280 192,1314 199,1347 213,1377 233,1402 252,1427 279,1446 313,1461 347,1475 391,1482 445,1482 466,1482 489,1481 512,1479 535,1477 555,1474 572,1470 L 572,1333 C 561,1335 548,1337 533,1339 518,1340 504,1341 492,1341 465,1341 444,1337 427,1330 410,1323 396,1312 387,1299 377,1285 370,1268 367,1248 363,1228 361,1205 361,1179 L 361,1082 572,1082 572,951 361,951 Z"/>
+   <glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,446 282,394 294,347 305,299 323,258 348,224 372,189 403,163 441,144 479,125 525,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 1008,206 992,176 972,146 951,115 924,88 890,64 856,39 814,19 763,4 712,-12 650,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,649 100,735 125,806 150,876 185,933 229,977 273,1021 324,1053 383,1073 442,1092 504,1102 571,1102 662,1102 738,1087 799,1058 860,1029 909,988 946,937 983,885 1009,824 1025,754 1040,684 1048,608 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 538,969 507,964 474,955 441,945 410,928 382,903 354,878 330,845 311,803 292,760 281,706 278,641 L 862,641 Z"/>
+   <glyph unicode="d" horiz-adv-x="954" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 C 823,921 823,931 823,946 822,960 822,975 822,991 821,1006 821,1021 821,1035 821,1049 821,1059 821,1065 L 821,1484 1001,1484 1001,223 C 1001,197 1001,172 1002,148 1002,124 1002,102 1003,82 1004,62 1004,45 1005,31 1006,16 1006,6 1007,0 L 835,0 C 834,7 833,16 832,29 831,41 830,55 829,71 828,87 827,104 826,122 825,139 825,157 825,174 L 821,174 Z M 275,542 C 275,467 280,403 289,350 298,297 313,253 334,219 355,184 381,159 413,143 445,127 484,119 530,119 577,119 619,127 656,142 692,157 722,182 747,217 771,251 789,296 802,351 815,406 821,474 821,554 821,631 815,696 802,749 789,802 771,844 746,877 721,910 691,933 656,948 620,962 579,969 532,969 488,969 450,961 418,946 386,931 359,906 338,872 317,838 301,794 291,740 280,685 275,619 275,542 Z"/>
+   <glyph unicode="c" horiz-adv-x="875" d="M 275,546 C 275,484 280,427 289,375 298,323 313,278 334,241 355,203 384,174 419,153 454,132 497,122 548,122 612,122 666,139 709,173 752,206 778,258 788,328 L 970,328 C 964,283 951,239 931,197 911,155 884,118 850,86 815,54 773,28 724,9 675,-10 618,-20 553,-20 468,-20 396,-6 337,23 278,52 230,91 193,142 156,192 129,251 112,320 95,388 87,462 87,542 87,615 93,679 105,735 117,790 134,839 156,881 177,922 203,957 232,986 261,1014 293,1037 328,1054 362,1071 398,1083 436,1091 474,1098 512,1102 551,1102 612,1102 666,1094 713,1077 760,1060 801,1038 836,1009 870,980 898,945 919,906 940,867 955,824 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 495,961 452,953 418,936 383,919 355,893 334,859 313,824 298,781 289,729 280,677 275,616 275,546 Z"/>
+   <glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,151 316,133 315,114 314,95 313,78 312,62 311,46 310,32 309,21 308,10 307,3 306,0 L 132,0 C 133,6 133,16 134,31 135,45 135,62 136,82 137,102 137,124 138,148 138,172 138,197 138,223 L 138,1484 318,1484 318,1061 C 318,1041 318,1022 318,1004 317,985 317,969 316,955 315,938 315,923 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,615 859,679 850,732 841,785 826,829 805,864 784,898 758,923 726,939 694,955 655,963 609,963 562,963 520,955 484,940 447,925 417,900 393,866 368,832 350,787 337,732 324,677 318,609 318,529 318,452 324,387 337,334 350,281 368,239 393,206 417,173 447,149 483,135 519,120 560,113 607,113 651,113 689,121 721,136 753,151 780,176 801,210 822,244 838,288 849,343 859,397 864,463 864,540 Z"/>
+   <glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,124 87,203 87,303 87,375 101,434 128,480 155,526 190,562 234,588 277,614 327,632 383,642 439,652 496,657 554,657 L 797,657 797,717 C 797,762 792,800 783,832 774,863 759,889 740,908 721,928 697,942 668,951 639,960 604,965 565,965 530,965 499,963 471,958 443,953 419,944 398,931 377,918 361,900 348,878 335,855 327,827 323,793 L 135,810 C 142,853 154,892 173,928 192,963 218,994 253,1020 287,1046 330,1066 382,1081 433,1095 496,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1090,111 1100,112 1110,113 1120,114 1130,116 1139,118 L 1139,6 C 1116,1 1094,-3 1072,-6 1049,-9 1025,-10 1000,-10 966,-10 937,-5 913,4 888,13 868,26 853,45 838,63 826,86 818,113 810,140 805,171 803,207 L 797,207 C 778,172 757,141 734,113 711,85 684,61 653,42 622,22 588,7 549,-4 510,-15 465,-20 414,-20 Z M 455,115 C 512,115 563,125 606,146 649,167 684,194 713,226 741,259 762,294 776,332 790,371 797,408 797,443 L 797,531 600,531 C 556,531 514,528 475,522 435,517 400,506 370,489 340,472 316,449 299,418 281,388 272,349 272,300 272,241 288,195 320,163 351,131 396,115 455,115 Z"/>
+   <glyph unicode="Z" horiz-adv-x="1139" d="M 1187,0 L 65,0 65,143 923,1253 138,1253 138,1409 1140,1409 1140,1270 282,156 1187,156 1187,0 Z"/>
+   <glyph unicode="Y" horiz-adv-x="1244" d="M 777,584 L 777,0 587,0 587,584 45,1409 255,1409 684,738 1111,1409 1321,1409 777,584 Z"/>
+   <glyph unicode="X" horiz-adv-x="1324" d="M 1112,0 L 689,616 257,0 46,0 582,732 87,1409 298,1409 690,856 1071,1409 1282,1409 800,739 1323,0 1112,0 Z"/>
+   <glyph unicode="T" horiz-adv-x="1139" d="M 720,1253 L 720,0 530,0 530,1253 46,1253 46,1409 1204,1409 1204,1253 720,1253 Z"/>
+   <glyph unicode="S" horiz-adv-x="1139" d="M 1272,389 C 1272,330 1261,275 1238,225 1215,175 1179,132 1131,96 1083,59 1023,31 950,11 877,-10 790,-20 690,-20 515,-20 378,11 280,72 182,133 120,222 93,338 L 278,375 C 287,338 302,305 321,275 340,245 367,219 400,198 433,176 473,159 522,147 571,135 629,129 697,129 754,129 806,134 853,144 900,153 941,168 975,188 1009,208 1036,234 1055,266 1074,297 1083,335 1083,379 1083,425 1073,462 1052,491 1031,520 1001,543 963,562 925,581 880,596 827,609 774,622 716,635 652,650 613,659 573,668 534,679 494,689 456,701 420,716 383,730 349,747 317,766 285,785 257,809 234,836 211,863 192,894 179,930 166,965 159,1006 159,1053 159,1120 173,1177 200,1225 227,1272 264,1311 312,1342 360,1373 417,1395 482,1409 547,1423 618,1430 694,1430 781,1430 856,1423 918,1410 980,1396 1032,1375 1075,1348 1118,1321 1152,1287 1178,1247 1203,1206 1224,1159 1239,1106 L 1051,1073 C 1042,1107 1028,1137 1011,1164 993,1191 970,1213 941,1231 912,1249 878,1263 837,1272 796,1281 747,1286 692,1286 627,1286 572,1280 528,1269 483,1257 448,1241 421,1221 394,1201 374,1178 363,1151 351,1124 345,1094 345,1063 345,1021 356,987 377,960 398,933 426,910 462,892 498,874 540,859 587,847 634,835 685,823 738,811 781,801 825,791 868,781 911,770 952,758 991,744 1030,729 1067,712 1102,693 1136,674 1166,650 1191,622 1216,594 1236,561 1251,523 1265,485 1272,440 1272,389 Z"/>
+   <glyph unicode="R" horiz-adv-x="1218" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 911,1409 982,1400 1044,1382 1105,1363 1157,1337 1199,1302 1241,1267 1273,1225 1295,1175 1317,1125 1328,1069 1328,1006 1328,961 1322,917 1309,874 1296,831 1275,791 1247,755 1219,719 1183,688 1140,662 1097,636 1045,618 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1047 1129,1084 1114,1115 1099,1146 1078,1173 1050,1194 1022,1215 988,1230 948,1241 908,1251 863,1256 812,1256 L 359,1256 359,736 820,736 C 875,736 922,743 962,757 1002,770 1035,789 1061,813 1086,837 1105,865 1118,898 1130,931 1136,966 1136,1004 Z"/>
+   <glyph unicode="P" horiz-adv-x="1086" d="M 1258,985 C 1258,924 1248,867 1228,814 1207,761 1177,715 1137,676 1096,637 1046,606 985,583 924,560 854,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 844,1409 917,1399 979,1379 1041,1358 1093,1330 1134,1293 1175,1256 1206,1211 1227,1159 1248,1106 1258,1048 1258,985 Z M 1066,983 C 1066,1072 1039,1140 984,1187 929,1233 847,1256 738,1256 L 359,1256 359,700 746,700 C 856,700 937,724 989,773 1040,822 1066,892 1066,983 Z"/>
+   <glyph unicode="O" horiz-adv-x="1377" d="M 1495,711 C 1495,601 1479,501 1448,411 1416,321 1370,244 1310,180 1250,116 1177,67 1090,32 1003,-3 905,-20 795,-20 679,-20 577,-2 490,35 403,71 330,122 272,187 214,252 170,329 141,418 112,507 97,605 97,711 97,821 112,920 143,1009 174,1098 219,1173 278,1236 337,1298 411,1346 498,1380 585,1413 684,1430 797,1430 909,1430 1009,1413 1096,1379 1183,1345 1256,1297 1315,1234 1374,1171 1418,1096 1449,1007 1480,918 1495,820 1495,711 Z M 1300,711 C 1300,796 1289,873 1268,942 1246,1011 1214,1071 1172,1120 1129,1169 1077,1207 1014,1234 951,1261 879,1274 797,1274 713,1274 639,1261 576,1234 513,1207 460,1169 418,1120 375,1071 344,1011 323,942 302,873 291,796 291,711 291,626 302,549 324,479 345,408 377,348 420,297 462,246 515,206 578,178 641,149 713,135 795,135 883,135 959,149 1023,178 1086,207 1139,247 1180,298 1221,349 1251,409 1271,480 1290,551 1300,628 1300,711 Z"/>
+   <glyph unicode="N" horiz-adv-x="1138" d="M 1082,0 L 328,1200 C 329,1167 331,1135 333,1103 334,1076 336,1047 337,1017 338,986 338,959 338,936 L 338,0 168,0 168,1409 390,1409 1152,201 C 1150,234 1148,266 1146,299 1145,327 1143,358 1142,391 1141,424 1140,455 1140,485 L 1140,1409 1312,1409 1312,0 1082,0 Z"/>
+   <glyph unicode="M" horiz-adv-x="1324" d="M 1366,0 L 1366,940 C 1366,974 1366,1009 1367,1044 1368,1079 1369,1112 1370,1141 1371,1175 1373,1208 1375,1240 1366,1206 1356,1172 1346,1139 1337,1110 1328,1080 1318,1048 1307,1015 1297,986 1287,960 L 923,0 789,0 420,960 C 416,970 412,982 408,995 403,1008 399,1023 394,1038 389,1053 384,1068 379,1084 374,1099 369,1115 364,1130 353,1165 342,1202 331,1240 332,1203 333,1166 334,1129 335,1098 336,1065 337,1031 338,996 338,966 338,940 L 338,0 168,0 168,1409 419,1409 794,432 C 799,419 804,402 811,381 818,360 824,338 830,316 836,294 842,273 847,254 852,234 855,219 857,208 859,219 863,234 868,254 873,274 880,295 887,317 894,339 900,360 907,381 914,402 920,419 925,432 L 1293,1409 1538,1409 1538,0 1366,0 Z"/>
+   <glyph unicode="L" horiz-adv-x="900" d="M 168,0 L 168,1409 359,1409 359,156 1071,156 1071,0 168,0 Z"/>
+   <glyph unicode="J" horiz-adv-x="848" d="M 457,-20 C 343,-20 250,10 177,69 104,128 55,222 32,350 L 219,381 C 226,338 237,301 252,270 267,239 286,213 307,193 328,173 352,158 378,149 404,140 431,135 458,135 527,135 582,159 622,207 662,254 682,324 682,416 L 682,1253 411,1253 411,1409 872,1409 872,420 C 872,353 863,292 844,238 825,184 798,138 763,100 727,61 683,32 632,11 581,-10 522,-20 457,-20 Z"/>
+   <glyph unicode="I" horiz-adv-x="186" d="M 189,0 L 189,1409 380,1409 380,0 189,0 Z"/>
+   <glyph unicode="F" horiz-adv-x="1006" d="M 359,1253 L 359,729 1145,729 1145,571 359,571 359,0 168,0 168,1409 1169,1409 1169,1253 359,1253 Z"/>
+   <glyph unicode="D" horiz-adv-x="1218" d="M 1381,719 C 1381,602 1363,498 1328,409 1293,319 1244,244 1183,184 1122,123 1049,78 966,47 882,16 792,0 695,0 L 168,0 168,1409 634,1409 C 743,1409 843,1396 935,1369 1026,1342 1105,1300 1171,1244 1237,1187 1289,1116 1326,1029 1363,942 1381,839 1381,719 Z M 1189,719 C 1189,814 1175,896 1148,964 1121,1031 1082,1087 1033,1130 984,1173 925,1205 856,1226 787,1246 712,1256 630,1256 L 359,1256 359,153 673,153 C 747,153 816,165 879,189 942,213 996,249 1042,296 1088,343 1124,402 1150,473 1176,544 1189,626 1189,719 Z"/>
+   <glyph unicode="C" horiz-adv-x="1297" d="M 792,1274 C 712,1274 641,1261 580,1234 518,1207 466,1169 425,1120 383,1071 351,1011 330,942 309,873 298,796 298,711 298,626 310,549 333,479 356,408 389,348 432,297 475,246 527,207 590,179 652,151 722,137 800,137 855,137 905,144 950,159 995,173 1035,193 1072,219 1108,245 1140,276 1169,312 1198,347 1223,387 1245,430 L 1401,352 C 1376,299 1344,250 1307,205 1270,160 1226,120 1176,87 1125,54 1068,28 1005,9 941,-10 870,-20 791,-20 677,-20 577,-2 492,35 406,71 334,122 277,187 219,252 176,329 147,418 118,507 104,605 104,711 104,821 119,920 150,1009 180,1098 224,1173 283,1236 341,1298 413,1346 498,1380 583,1413 681,1430 790,1430 940,1430 1065,1401 1166,1342 1267,1283 1341,1196 1388,1081 L 1207,1021 C 1194,1054 1176,1086 1153,1117 1130,1147 1102,1174 1068,1197 1034,1220 994,1239 949,1253 903,1267 851,1274 792,1274 Z"/>
+   <glyph unicode="A" horiz-adv-x="1350" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 768,1026 C 757,1053 747,1080 738,1107 728,1134 719,1159 712,1182 705,1204 699,1223 694,1238 689,1253 686,1262 685,1265 684,1262 681,1252 676,1237 671,1222 665,1203 658,1180 650,1157 641,1132 632,1105 622,1078 612,1051 602,1024 L 422,561 949,561 768,1026 Z"/>
+   <glyph unicode="4" horiz-adv-x="1033" d="M 881,319 L 881,0 711,0 711,319 47,319 47,459 692,1409 881,1409 881,461 1079,461 1079,319 881,319 Z M 711,1206 C 710,1203 706,1196 701,1187 696,1177 690,1166 683,1154 676,1142 670,1130 663,1118 656,1105 649,1095 644,1087 L 283,555 C 280,550 275,543 269,534 262,525 256,517 249,508 242,499 236,490 229,481 222,472 217,466 213,461 L 711,461 711,1206 Z"/>
+   <glyph unicode="3" horiz-adv-x="980" d="M 1049,389 C 1049,324 1039,267 1018,216 997,165 966,123 926,88 885,53 835,26 776,8 716,-11 648,-20 571,-20 484,-20 410,-9 351,13 291,34 242,63 203,99 164,134 135,175 116,221 97,266 84,313 78,362 L 264,379 C 269,342 279,308 294,277 308,246 327,220 352,198 377,176 407,159 443,147 479,135 522,129 571,129 662,129 733,151 785,196 836,241 862,307 862,395 862,447 851,489 828,521 805,552 776,577 742,595 707,612 670,624 630,630 589,636 552,639 518,639 L 416,639 416,795 514,795 C 548,795 583,799 620,806 657,813 690,825 721,844 751,862 776,887 796,918 815,949 825,989 825,1038 825,1113 803,1173 759,1217 714,1260 648,1282 561,1282 482,1282 418,1262 369,1221 320,1180 291,1123 283,1049 L 102,1063 C 109,1125 126,1179 153,1225 180,1271 214,1309 255,1340 296,1370 342,1393 395,1408 448,1423 504,1430 563,1430 642,1430 709,1420 766,1401 823,1381 869,1354 905,1321 941,1287 968,1247 985,1202 1002,1157 1010,1108 1010,1057 1010,1016 1004,977 993,941 982,905 964,873 940,844 916,815 886,791 849,770 812,749 767,734 715,723 L 715,719 C 772,713 821,700 863,681 905,661 940,636 967,607 994,578 1015,544 1029,507 1042,470 1049,430 1049,389 Z"/>
+   <glyph unicode="2" horiz-adv-x="927" d="M 103,0 L 103,127 C 137,205 179,274 228,334 277,393 328,447 382,496 436,544 490,589 543,630 596,671 643,713 686,754 729,795 763,839 790,884 816,929 829,981 829,1038 829,1078 823,1113 811,1144 799,1174 782,1199 759,1220 736,1241 709,1256 678,1267 646,1277 611,1282 572,1282 536,1282 502,1277 471,1267 439,1257 411,1242 386,1222 361,1202 341,1177 326,1148 310,1118 300,1083 295,1044 L 111,1061 C 117,1112 131,1159 153,1204 175,1249 205,1288 244,1322 283,1355 329,1382 384,1401 438,1420 501,1430 572,1430 642,1430 704,1422 759,1405 814,1388 860,1364 898,1331 935,1298 964,1258 984,1210 1004,1162 1014,1107 1014,1044 1014,997 1006,952 989,909 972,866 949,826 921,787 892,748 859,711 822,675 785,639 746,604 705,570 664,535 623,501 582,468 541,434 502,400 466,366 429,332 397,298 368,263 339,228 317,191 301,153 L 1036,153 1036,0 103,0 Z"/>
+   <glyph unicode="1" horiz-adv-x="874" d="M 156,0 L 156,153 515,153 515,1237 197,1010 197,1180 530,1409 696,1409 696,153 1039,153 1039,0 156,0 Z"/>
+   <glyph unicode="/" horiz-adv-x="583" d="M 0,-20 L 411,1484 569,1484 162,-20 0,-20 Z"/>
+   <glyph unicode="." horiz-adv-x="186" d="M 187,0 L 187,219 382,219 382,0 187,0 Z"/>
+   <glyph unicode="," horiz-adv-x="212" d="M 385,219 L 385,51 C 385,16 384,-16 381,-46 378,-74 373,-101 366,-127 359,-151 351,-175 342,-197 332,-219 320,-241 307,-262 L 184,-262 C 214,-219 237,-175 254,-131 270,-87 278,-43 278,0 L 190,0 190,219 385,219 Z"/>
+   <glyph unicode=")" horiz-adv-x="557" d="M 555,528 C 555,435 548,346 534,262 520,177 498,96 468,18 438,-60 400,-136 353,-209 306,-282 251,-354 186,-424 L 12,-424 C 75,-354 129,-282 175,-209 220,-136 258,-60 287,19 316,98 338,179 353,264 367,349 374,437 374,530 374,623 367,711 353,796 338,881 316,962 287,1041 258,1119 220,1195 175,1269 129,1342 75,1414 12,1484 L 186,1484 C 251,1414 306,1342 353,1269 400,1196 438,1120 468,1042 498,964 520,883 534,798 548,713 555,625 555,532 L 555,528 Z"/>
+   <glyph unicode="(" horiz-adv-x="557" d="M 127,532 C 127,625 134,713 148,798 162,883 184,964 214,1042 244,1120 282,1196 329,1269 376,1342 431,1414 496,1484 L 670,1484 C 607,1414 553,1342 508,1269 462,1195 424,1119 395,1041 366,962 344,881 330,796 315,711 308,623 308,530 308,437 315,349 330,264 344,179 366,98 395,19 424,-60 462,-136 508,-209 553,-282 607,-354 670,-424 L 496,-424 C 431,-354 376,-282 329,-209 282,-136 244,-60 214,18 184,96 162,177 148,262 134,346 127,435 127,528 L 127,532 Z"/>
+   <glyph unicode=" " horiz-adv-x="556"/>
+  </font>
+ </defs>
+ <defs class="TextShapeIndex">
+  <g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45 id46 id47 id48 id49"/>
+ </defs>
+ <defs class="EmbeddedBulletChars">
+  <g id="bullet-char-template(57356)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
+  </g>
+  <g id="bullet-char-template(57354)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
+  </g>
+  <g id="bullet-char-template(10146)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
+  </g>
+  <g id="bullet-char-template(10132)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
+  </g>
+  <g id="bullet-char-template(10007)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
+  </g>
+  <g id="bullet-char-template(10004)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
+  </g>
+  <g id="bullet-char-template(9679)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
+  </g>
+  <g id="bullet-char-template(8226)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
+  </g>
+  <g id="bullet-char-template(8211)" transform="scale(0.00048828125,-0.00048828125)">
+   <path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
+  </g>
+ </defs>
+ <defs class="TextEmbeddedBitmaps"/>
+ <g>
+  <g id="id2" class="Master_Slide">
+   <g id="bg-id2" class="Background"/>
+   <g id="bo-id2" class="BackgroundObjects"/>
+  </g>
+ </g>
+ <g class="SlideGroup">
+  <g>
+   <g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
+    <g class="Page">
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id3">
+       <rect class="BoundingBox" stroke="none" fill="none" x="1748" y="12039" width="19180" height="2543"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 11338,14580 L 1749,14580 1749,12040 20926,12040 20926,14580 11338,14580 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 11338,14580 L 1749,14580 1749,12040 20926,12040 20926,14580 11338,14580 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id4">
+       <rect class="BoundingBox" stroke="none" fill="none" x="8426" y="12673" width="5845" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 11348,14325 L 8427,14325 8427,12674 14269,12674 14269,14325 11348,14325 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 11348,14325 L 8427,14325 8427,12674 14269,12674 14269,14325 11348,14325 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="10626" y="13277"/><tspan class="TextPosition" x="10626" y="13277"><tspan fill="rgb(255,255,255)" stroke="none">.YAML</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9340" y="14095"/><tspan class="TextPosition" x="9340" y="14095"><tspan fill="rgb(255,255,255)" stroke="none">Specification (CSIT gerrit)</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id5">
+       <rect class="BoundingBox" stroke="none" fill="none" x="1815" y="11940" width="1553" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2065" y="12514"/><tspan class="TextPosition" x="2065" y="12514"><tspan fill="rgb(255,255,255)" stroke="none">Data</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id6">
+       <rect class="BoundingBox" stroke="none" fill="none" x="2026" y="12673" width="5845" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 4948,14325 L 2027,14325 2027,12674 7869,12674 7869,14325 4948,14325 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 4948,14325 L 2027,14325 2027,12674 7869,12674 7869,14325 4948,14325 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="4383" y="13277"/><tspan class="TextPosition" x="4383" y="13277"><tspan fill="rgb(255,255,255)" stroke="none">.RST</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="2872" y="14095"/><tspan class="TextPosition" x="2872" y="14095"><tspan fill="rgb(255,255,255)" stroke="none">Static content (CSIT gerrit)</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id7">
+       <rect class="BoundingBox" stroke="none" fill="none" x="14826" y="12673" width="5845" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 17748,14325 L 14827,14325 14827,12674 20669,12674 20669,14325 17748,14325 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 17748,14325 L 14827,14325 14827,12674 20669,12674 20669,14325 17748,14325 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="16485" y="13277"/><tspan class="TextPosition" x="16485" y="13277"><tspan fill="rgb(255,255,255)" stroke="none">.ZIP (.XML)</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="15750" y="14095"/><tspan class="TextPosition" x="15750" y="14095"><tspan fill="rgb(255,255,255)" stroke="none">Data to process (Jenkins)</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id8">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4289" y="8239" width="11433" height="2543"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 10005,10780 L 4290,10780 4290,8240 15720,8240 15720,10780 10005,10780 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10005,10780 L 4290,10780 4290,8240 15720,8240 15720,10780 10005,10780 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id9">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4289" y="4439" width="11433" height="2543"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 10005,6980 L 4290,6980 4290,4440 15720,4440 15720,6980 10005,6980 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 10005,6980 L 4290,6980 4290,4440 15720,4440 15720,6980 10005,6980 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id10">
+       <rect class="BoundingBox" stroke="none" fill="none" x="1749" y="639" width="13973" height="2543"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 8735,3180 L 1750,3180 1750,640 15720,640 15720,3180 8735,3180 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 8735,3180 L 1750,3180 1750,640 15720,640 15720,3180 8735,3180 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id11">
+       <rect class="BoundingBox" stroke="none" fill="none" x="16099" y="609" width="4822" height="10173"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 18510,10780 L 16100,10780 16100,610 20919,610 20919,10780 18510,10780 Z"/>
+       <path fill="none" stroke="rgb(52,101,164)" d="M 18510,10780 L 16100,10780 16100,610 20919,610 20919,10780 18510,10780 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id12">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4525" y="8873" width="10942" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 9996,10525 L 4526,10525 4526,8874 15465,8874 15465,10525 9996,10525 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 9996,10525 L 4526,10525 4526,8874 15465,8874 15465,10525 9996,10525 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="9183" y="9477"/><tspan class="TextPosition" x="9183" y="9477"><tspan fill="rgb(255,255,255)" stroke="none">pandas</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="8393" y="9901"/><tspan class="TextPosition" x="8393" y="9901"><tspan fill="rgb(255,255,255)" stroke="none">Data model in JSON</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6817" y="10295"/><tspan class="TextPosition" x="6817" y="10295"><tspan fill="rgb(255,255,255)" stroke="none">Specification, Input data (Pandas.Series)</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id13">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4315" y="8140" width="5437" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="4565" y="8714"/><tspan class="TextPosition" x="4565" y="8714"><tspan fill="rgb(255,255,255)" stroke="none">Data processing</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id14">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4315" y="4340" width="5437" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="4565" y="4914"/><tspan class="TextPosition" x="4565" y="4914"><tspan fill="rgb(255,255,255)" stroke="none">Data presentation</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id15">
+       <rect class="BoundingBox" stroke="none" fill="none" x="4525" y="5073" width="2560" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 5805,6725 L 4526,6725 4526,5074 7083,5074 7083,6725 5805,6725 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 5805,6725 L 4526,6725 4526,5074 7083,5074 7083,6725 5805,6725 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="5252" y="5677"/><tspan class="TextPosition" x="5252" y="5677"><tspan fill="rgb(255,255,255)" stroke="none">Plots</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="4697" y="6495"/><tspan class="TextPosition" x="4697" y="6495"><tspan fill="rgb(255,255,255)" stroke="none">plot.ly â†’ .html</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id16">
+       <rect class="BoundingBox" stroke="none" fill="none" x="8826" y="5073" width="2451" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 10051,6725 L 8827,6725 8827,5074 11275,5074 11275,6725 10051,6725 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 10051,6725 L 8827,6725 8827,5074 11275,5074 11275,6725 10051,6725 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="9528" y="5677"/><tspan class="TextPosition" x="9528" y="5677"><tspan fill="rgb(255,255,255)" stroke="none">Files</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9649" y="6495"/><tspan class="TextPosition" x="9649" y="6495"><tspan fill="rgb(255,255,255)" stroke="none">.RST</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id17">
+       <rect class="BoundingBox" stroke="none" fill="none" x="12925" y="5073" width="2560" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 14205,6725 L 12926,6725 12926,5074 15483,5074 15483,6725 14205,6725 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 14205,6725 L 12926,6725 12926,5074 15483,5074 15483,6725 14205,6725 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13489" y="5677"/><tspan class="TextPosition" x="13489" y="5677"><tspan fill="rgb(255,255,255)" stroke="none">Tables</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="13023" y="6495"/><tspan class="TextPosition" x="13023" y="6495"><tspan fill="rgb(255,255,255)" stroke="none">Pandas â†’ .csv</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id18">
+       <rect class="BoundingBox" stroke="none" fill="none" x="1715" y="540" width="5437" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="1965" y="1114"/><tspan class="TextPosition" x="1965" y="1114"><tspan fill="rgb(255,255,255)" stroke="none">Report generation</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id19">
+       <rect class="BoundingBox" stroke="none" fill="none" x="2003" y="1273" width="13465" height="1654"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 8735,2925 L 2004,2925 2004,1274 15466,1274 15466,2925 8735,2925 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 8735,2925 L 2004,2925 2004,1274 15466,1274 15466,2925 8735,2925 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="7982" y="1877"/><tspan class="TextPosition" x="7982" y="1877"><tspan fill="rgb(255,255,255)" stroke="none">Sphinx</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6163" y="2695"/><tspan class="TextPosition" x="6163" y="2695"><tspan fill="rgb(255,255,255)" stroke="none">.html / .pdf (then stored in nexus)</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id20">
+       <rect class="BoundingBox" stroke="none" fill="none" x="16015" y="540" width="4659" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="16265" y="1114"/><tspan class="TextPosition" x="16265" y="1114"><tspan fill="rgb(255,255,255)" stroke="none">Jenkins plots</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.CustomShape">
+      <g id="id21">
+       <rect class="BoundingBox" stroke="none" fill="none" x="16354" y="1243" width="4317" height="9284"/>
+       <path fill="rgb(114,159,207)" stroke="none" d="M 18512,10525 L 16355,10525 16355,1244 20669,1244 20669,10525 18512,10525 Z"/>
+       <path fill="none" stroke="rgb(255,255,255)" d="M 18512,10525 L 16355,10525 16355,1244 20669,1244 20669,10525 18512,10525 Z"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="17200" y="5385"/><tspan class="TextPosition" x="17200" y="5385"><tspan fill="rgb(255,255,255)" stroke="none">Jenkins plot</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="17852" y="5940"/><tspan class="TextPosition" x="17852" y="5940"><tspan fill="rgb(255,255,255)" stroke="none">plugin</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="18129" y="6758"/><tspan class="TextPosition" x="18129" y="6758"><tspan fill="rgb(255,255,255)" stroke="none">.html</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id22">
+       <rect class="BoundingBox" stroke="none" fill="none" x="415" y="11840" width="1303" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="665" y="12414"/><tspan class="TextPosition" x="665" y="12414"><tspan fill="rgb(0,0,0)" stroke="none">sL1</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id23">
+       <rect class="BoundingBox" stroke="none" fill="none" x="415" y="8140" width="1303" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="665" y="8714"/><tspan class="TextPosition" x="665" y="8714"><tspan fill="rgb(0,0,0)" stroke="none">sL2</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id24">
+       <rect class="BoundingBox" stroke="none" fill="none" x="415" y="4340" width="1303" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="665" y="4914"/><tspan class="TextPosition" x="665" y="4914"><tspan fill="rgb(0,0,0)" stroke="none">sL3</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id25">
+       <rect class="BoundingBox" stroke="none" fill="none" x="415" y="540" width="1303" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="665" y="1114"/><tspan class="TextPosition" x="665" y="1114"><tspan fill="rgb(0,0,0)" stroke="none">sL4</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id26">
+       <rect class="BoundingBox" stroke="none" fill="none" x="3031" y="2896" width="431" height="10007"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 3222,12548 L 3271,3439"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 3257,12528 L 3291,12537 3325,12555 3356,12579 3380,12610 3397,12645 3406,12679 3411,12714 3406,12748 3396,12784 3379,12818 3355,12847 3323,12871 3289,12888 3255,12899 3219,12902 3185,12899 3150,12887 3117,12870 3086,12846 3062,12816 3045,12782 3036,12746 3032,12712 3036,12677 3046,12643 3063,12609 3088,12578 3119,12554 3152,12537 3187,12527 3221,12523 3257,12528 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 3274,2896 L 3081,3463 3460,3466 3274,2896 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id27">
+       <rect class="BoundingBox" stroke="none" fill="none" x="11096" y="10516" width="411" height="2388"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 11313,12548 L 11285,11059"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 11348,12527 L 11382,12536 11416,12552 11448,12576 11473,12607 11491,12640 11501,12674 11506,12709 11502,12744 11493,12779 11477,12814 11453,12844 11422,12869 11389,12886 11355,12898 11319,12902 11285,12900 11250,12889 11216,12873 11185,12849 11160,12820 11142,12786 11132,12751 11127,12717 11131,12681 11140,12647 11156,12612 11180,12581 11210,12556 11243,12538 11278,12528 11312,12523 11348,12527 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 11275,10516 L 11096,11088 11475,11081 11275,10516 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id28">
+       <rect class="BoundingBox" stroke="none" fill="none" x="15000" y="10517" width="411" height="2388"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 15217,12549 L 15189,11060"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 15252,12528 L 15286,12537 15320,12553 15352,12577 15377,12608 15395,12641 15405,12675 15410,12710 15406,12745 15397,12780 15381,12815 15357,12845 15326,12870 15293,12887 15259,12899 15223,12903 15189,12901 15154,12890 15120,12874 15089,12850 15064,12821 15046,12787 15036,12752 15031,12718 15035,12682 15044,12648 15060,12613 15084,12582 15114,12557 15147,12539 15182,12529 15216,12524 15252,12528 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 15179,10517 L 15000,11089 15379,11082 15179,10517 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id29">
+       <rect class="BoundingBox" stroke="none" fill="none" x="18304" y="10518" width="411" height="2388"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 18521,12550 L 18493,11061"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 18556,12529 L 18590,12538 18624,12554 18656,12578 18681,12609 18699,12642 18709,12676 18714,12711 18710,12746 18701,12781 18685,12816 18661,12846 18630,12871 18597,12888 18563,12900 18527,12904 18493,12902 18458,12891 18424,12875 18393,12851 18368,12822 18350,12788 18340,12753 18335,12719 18339,12683 18348,12649 18364,12614 18388,12583 18418,12558 18451,12540 18486,12530 18520,12525 18556,12529 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 18483,10518 L 18304,11090 18683,11083 18483,10518 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id30">
+       <rect class="BoundingBox" stroke="none" fill="none" x="5497" y="2896" width="381" height="2414"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 5687,4955 L 5687,3439"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 5722,4934 L 5756,4944 5790,4961 5821,4985 5845,5016 5862,5051 5872,5085 5877,5120 5872,5154 5862,5190 5845,5224 5821,5254 5790,5278 5756,5295 5722,5306 5686,5309 5652,5306 5617,5295 5584,5278 5553,5254 5529,5224 5512,5190 5502,5154 5498,5120 5502,5085 5512,5051 5529,5016 5553,4985 5584,4961 5617,4944 5652,4934 5686,4930 5722,4934 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 5687,2896 L 5498,3465 5877,3465 5687,2896 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id31">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9815" y="2896" width="381" height="2414"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 10005,4955 L 10005,3439"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 10040,4934 L 10074,4944 10108,4961 10139,4985 10163,5016 10180,5051 10190,5085 10195,5120 10190,5154 10180,5190 10163,5224 10139,5254 10108,5278 10074,5295 10040,5306 10004,5309 9970,5306 9935,5295 9902,5278 9871,5254 9847,5224 9830,5190 9820,5154 9816,5120 9820,5085 9830,5051 9847,5016 9871,4985 9902,4961 9935,4944 9970,4934 10004,4930 10040,4934 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 10005,2896 L 9816,3465 10195,3465 10005,2896 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id32">
+       <rect class="BoundingBox" stroke="none" fill="none" x="13999" y="2919" width="387" height="2392"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 14195,4955 L 14189,3462"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 14229,4934 L 14263,4944 14298,4961 14329,4985 14353,5016 14370,5050 14380,5084 14385,5119 14381,5153 14371,5189 14354,5223 14330,5253 14299,5277 14265,5294 14231,5306 14195,5309 14161,5306 14126,5295 14093,5278 14062,5254 14037,5224 14020,5190 14010,5155 14006,5121 14010,5086 14020,5051 14037,5017 14061,4986 14092,4962 14124,4944 14160,4934 14194,4930 14229,4934 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 14187,2919 L 14000,3488 14379,3487 14187,2919 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id33">
+       <rect class="BoundingBox" stroke="none" fill="none" x="6957" y="5627" width="1906" height="381"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 7311,5817 L 8319,5817"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 7332,5852 L 7322,5886 7305,5920 7281,5951 7250,5975 7215,5992 7181,6002 7146,6007 7112,6002 7076,5992 7042,5975 7012,5951 6988,5920 6971,5886 6960,5852 6957,5816 6960,5782 6971,5747 6988,5714 7012,5683 7042,5659 7076,5642 7112,5632 7146,5628 7181,5632 7215,5642 7250,5659 7281,5683 7305,5714 7322,5747 7332,5782 7336,5816 7332,5852 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 8862,5817 L 8294,5628 8294,6007 8862,5817 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id34">
+       <rect class="BoundingBox" stroke="none" fill="none" x="11275" y="5627" width="1779" height="381"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 12699,5817 L 11818,5817"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 12678,5782 L 12688,5748 12705,5714 12729,5683 12760,5659 12795,5642 12829,5632 12864,5628 12898,5632 12934,5642 12968,5659 12998,5683 13022,5714 13039,5748 13050,5782 13053,5818 13050,5852 13039,5887 13022,5920 12998,5951 12968,5975 12934,5992 12898,6002 12864,6007 12829,6002 12795,5992 12760,5975 12729,5951 12705,5920 12688,5887 12678,5852 12674,5818 12678,5782 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 11275,5817 L 11844,6007 11844,5628 11275,5817 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id35">
+       <rect class="BoundingBox" stroke="none" fill="none" x="5524" y="6706" width="381" height="2160"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 5714,7249 L 5714,8322"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 5714,6706 L 5525,7275 5904,7275 5714,6706 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 5714,8865 L 5904,8297 5525,8297 5714,8865 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id36">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9824" y="6707" width="381" height="2160"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 10014,7250 L 10014,8323"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 10014,6707 L 9825,7276 10204,7276 10014,6707 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 10014,8866 L 10204,8298 9825,8298 10014,8866 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.LineShape">
+      <g id="id37">
+       <rect class="BoundingBox" stroke="none" fill="none" x="14024" y="6708" width="381" height="2160"/>
+       <path fill="none" stroke="rgb(0,102,204)" stroke-width="53" stroke-linejoin="round" d="M 14214,7251 L 14214,8324"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 14214,6708 L 14025,7277 14404,7277 14214,6708 Z"/>
+       <path fill="rgb(0,102,204)" stroke="none" d="M 14214,8867 L 14404,8299 14025,8299 14214,8867 Z"/>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id38">
+       <rect class="BoundingBox" stroke="none" fill="none" x="3115" y="11440" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="3365" y="11883"/><tspan class="TextPosition" x="3365" y="11883"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id39">
+       <rect class="BoundingBox" stroke="none" fill="none" x="11215" y="11440" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="11465" y="11883"/><tspan class="TextPosition" x="11465" y="11883"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id40">
+       <rect class="BoundingBox" stroke="none" fill="none" x="15115" y="11440" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="15365" y="11883"/><tspan class="TextPosition" x="15365" y="11883"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id41">
+       <rect class="BoundingBox" stroke="none" fill="none" x="18415" y="11440" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="18665" y="11883"/><tspan class="TextPosition" x="18665" y="11883"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id42">
+       <rect class="BoundingBox" stroke="none" fill="none" x="14115" y="3840" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="14365" y="4283"/><tspan class="TextPosition" x="14365" y="4283"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id43">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9915" y="3840" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10165" y="4283"/><tspan class="TextPosition" x="10165" y="4283"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id44">
+       <rect class="BoundingBox" stroke="none" fill="none" x="5615" y="3840" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="5865" y="4283"/><tspan class="TextPosition" x="5865" y="4283"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id45">
+       <rect class="BoundingBox" stroke="none" fill="none" x="6915" y="5140" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="7165" y="5583"/><tspan class="TextPosition" x="7165" y="5583"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id46">
+       <rect class="BoundingBox" stroke="none" fill="none" x="11115" y="5140" width="2065" height="807"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="11365" y="5583"/><tspan class="TextPosition" x="11365" y="5583"><tspan fill="rgb(0,69,134)" stroke="none">Read files</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id47">
+       <rect class="BoundingBox" stroke="none" fill="none" x="5615" y="7640" width="2613" height="718"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="5865" y="8083"/><tspan class="TextPosition" x="5865" y="8083"><tspan fill="rgb(0,69,134)" stroke="none">Python calls</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id48">
+       <rect class="BoundingBox" stroke="none" fill="none" x="9915" y="7641" width="2613" height="718"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10165" y="8084"/><tspan class="TextPosition" x="10165" y="8084"><tspan fill="rgb(0,69,134)" stroke="none">Python calls</tspan></tspan></tspan></text>
+      </g>
+     </g>
+     <g class="com.sun.star.drawing.TextShape">
+      <g id="id49">
+       <rect class="BoundingBox" stroke="none" fill="none" x="12915" y="7642" width="2613" height="718"/>
+       <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="13165" y="8085"/><tspan class="TextPosition" x="13165" y="8085"><tspan fill="rgb(0,69,134)" stroke="none">Python calls</tspan></tspan></tspan></text>
+      </g>
+     </g>
+    </g>
+   </g>
+  </g>
+ </g>
+</svg>
\ No newline at end of file
diff --git a/resources/tools/presentation/new/doc/pal_lld.rst b/resources/tools/presentation/new/doc/pal_lld.rst
new file mode 100644 (file)
index 0000000..81c2547
--- /dev/null
@@ -0,0 +1,1623 @@
+Presentation and Analytics Layer
+================================
+
+Overview
+--------
+
+The presentation and analytics layer (PAL) is the fourth layer of CSIT
+hierarchy. The model of presentation and analytics layer consists of four
+sub-layers, bottom up:
+
+ - sL1 - Data - input data to be processed:
+
+   - Static content - .rst text files, .svg static figures, and other files
+     stored in the CSIT git repository.
+   - Data to process - .xml files generated by Jenkins jobs executing tests,
+     stored as robot results files (output.xml).
+   - Specification - .yaml file with the models of report elements (tables,
+     plots, layout, ...) generated by this tool. There is also the configuration
+     of the tool and the specification of input data (jobs and builds).
+
+ - sL2 - Data processing
+
+   - The data are read from the specified input files (.xml) and stored as
+     multi-indexed `pandas.Series <https://pandas.pydata.org/pandas-docs/stable/
+     generated/pandas.Series.html>`_.
+   - This layer provides also interface to input data and filtering of the input
+     data.
+
+ - sL3 - Data presentation - This layer generates the elements specified in the
+   specification file:
+
+   - Tables: .csv files linked to static .rst files.
+   - Plots: .html files generated using plot.ly linked to static .rst files.
+
+ - sL4 - Report generation - Sphinx generates required formats and versions:
+
+   - formats: html, pdf
+   - versions: minimal, full (TODO: define the names and scope of versions)
+
+.. only:: latex
+
+    .. raw:: latex
+
+        \begin{figure}[H]
+        \centering
+            \includesvg[width=0.90\textwidth]{../_tmp/src/csit_framework_documentation/pal_layers}
+            \label{fig:pal_layers}
+        \end{figure}
+
+.. only:: html
+
+    .. figure:: pal_layers.svg
+        :alt: PAL Layers
+        :align: center
+
+Data
+----
+
+Report Specification
+````````````````````
+
+The report specification file defines which data is used and which outputs are
+generated. It is human readable and structured. It is easy to add / remove /
+change items. The specification includes:
+
+ - Specification of the environment.
+ - Configuration of debug mode (optional).
+ - Specification of input data (jobs, builds, files, ...).
+ - Specification of the output.
+ - What and how is generated:
+   - What: plots, tables.
+   - How: specification of all properties and parameters.
+ - .yaml format.
+
+Structure of the specification file
+'''''''''''''''''''''''''''''''''''
+
+The specification file is organized as a list of dictionaries distinguished by
+the type:
+
+::
+
+    -
+      type: "environment"
+    -
+      type: "configuration"
+    -
+      type: "debug"
+    -
+      type: "static"
+    -
+      type: "input"
+    -
+      type: "output"
+    -
+      type: "table"
+    -
+      type: "plot"
+    -
+      type: "file"
+
+Each type represents a section. The sections "environment", "debug", "static",
+"input" and "output" are listed only once in the specification; "table", "file"
+and "plot" can be there multiple times.
+
+Sections "debug", "table", "file" and "plot" are optional.
+
+Table(s), files(s) and plot(s) are referred as "elements" in this text. It is
+possible to define and implement other elements if needed.
+
+
+Section: Environment
+''''''''''''''''''''
+
+This section has the following parts:
+
+ - type: "environment" - says that this is the section "environment".
+ - configuration - configuration of the PAL.
+ - paths - paths used by the PAL.
+ - urls - urls pointing to the data sources.
+ - make-dirs - a list of the directories to be created by the PAL while
+   preparing the environment.
+ - remove-dirs - a list of the directories to be removed while cleaning the
+   environment.
+ - build-dirs - a list of the directories where the results are stored.
+
+The structure of the section "Environment" is as follows (example):
+
+::
+
+    -
+      type: "environment"
+      configuration:
+        # Debug mode:
+        # - Skip:
+        #   - Download of input data files
+        # - Do:
+        #   - Read data from given zip / xml files
+        #   - Set the configuration as it is done in normal mode
+        # If the section "type: debug" is missing, CFG[DEBUG] is set to 0.
+        CFG[DEBUG]: 0
+
+      paths:
+        # Top level directories:
+        ## Working directory
+        DIR[WORKING]: "_tmp"
+        ## Build directories
+        DIR[BUILD,HTML]: "_build"
+        DIR[BUILD,LATEX]: "_build_latex"
+
+        # Static .rst files
+        DIR[RST]: "../../../docs/report"
+
+        # Working directories
+        ## Input data files (.zip, .xml)
+        DIR[WORKING,DATA]: "{DIR[WORKING]}/data"
+        ## Static source files from git
+        DIR[WORKING,SRC]: "{DIR[WORKING]}/src"
+        DIR[WORKING,SRC,STATIC]: "{DIR[WORKING,SRC]}/_static"
+
+        # Static html content
+        DIR[STATIC]: "{DIR[BUILD,HTML]}/_static"
+        DIR[STATIC,VPP]: "{DIR[STATIC]}/vpp"
+        DIR[STATIC,DPDK]: "{DIR[STATIC]}/dpdk"
+        DIR[STATIC,ARCH]: "{DIR[STATIC]}/archive"
+
+        # Detailed test results
+        DIR[DTR]: "{DIR[WORKING,SRC]}/detailed_test_results"
+        DIR[DTR,PERF,DPDK]: "{DIR[DTR]}/dpdk_performance_results"
+        DIR[DTR,PERF,VPP]: "{DIR[DTR]}/vpp_performance_results"
+        DIR[DTR,PERF,HC]: "{DIR[DTR]}/honeycomb_performance_results"
+        DIR[DTR,FUNC,VPP]: "{DIR[DTR]}/vpp_functional_results"
+        DIR[DTR,FUNC,HC]: "{DIR[DTR]}/honeycomb_functional_results"
+        DIR[DTR,FUNC,NSHSFC]: "{DIR[DTR]}/nshsfc_functional_results"
+        DIR[DTR,PERF,VPP,IMPRV]: "{DIR[WORKING,SRC]}/vpp_performance_tests/performance_improvements"
+
+        # Detailed test configurations
+        DIR[DTC]: "{DIR[WORKING,SRC]}/test_configuration"
+        DIR[DTC,PERF,VPP]: "{DIR[DTC]}/vpp_performance_configuration"
+        DIR[DTC,FUNC,VPP]: "{DIR[DTC]}/vpp_functional_configuration"
+
+        # Detailed tests operational data
+        DIR[DTO]: "{DIR[WORKING,SRC]}/test_operational_data"
+        DIR[DTO,PERF,VPP]: "{DIR[DTO]}/vpp_performance_operational_data"
+
+        # .css patch file to fix tables generated by Sphinx
+        DIR[CSS_PATCH_FILE]: "{DIR[STATIC]}/theme_overrides.css"
+        DIR[CSS_PATCH_FILE2]: "{DIR[WORKING,SRC,STATIC]}/theme_overrides.css"
+
+      urls:
+        URL[JENKINS,CSIT]: "https://jenkins.fd.io/view/csit/job"
+        URL[JENKINS,HC]: "https://jenkins.fd.io/view/hc2vpp/job"
+
+      make-dirs:
+      # List the directories which are created while preparing the environment.
+      # All directories MUST be defined in "paths" section.
+      - "DIR[WORKING,DATA]"
+      - "DIR[STATIC,VPP]"
+      - "DIR[STATIC,DPDK]"
+      - "DIR[STATIC,ARCH]"
+      - "DIR[BUILD,LATEX]"
+      - "DIR[WORKING,SRC]"
+      - "DIR[WORKING,SRC,STATIC]"
+
+      remove-dirs:
+      # List the directories which are deleted while cleaning the environment.
+      # All directories MUST be defined in "paths" section.
+      #- "DIR[BUILD,HTML]"
+
+      build-dirs:
+      # List the directories where the results (build) is stored.
+      # All directories MUST be defined in "paths" section.
+      - "DIR[BUILD,HTML]"
+      - "DIR[BUILD,LATEX]"
+
+It is possible to use defined items in the definition of other items, e.g.:
+
+::
+
+    DIR[WORKING,DATA]: "{DIR[WORKING]}/data"
+
+will be automatically changed to
+
+::
+
+    DIR[WORKING,DATA]: "_tmp/data"
+
+
+Section: Configuration
+''''''''''''''''''''''
+
+This section specifies the groups of parameters which are repeatedly used in the
+elements defined later in the specification file. It has the following parts:
+
+ - data sets - Specification of data sets used later in element's specifications
+   to define the input data.
+ - plot layouts - Specification of plot layouts used later in plots'
+   specifications to define the plot layout.
+
+The structure of the section "Configuration" is as follows (example):
+
+::
+
+    -
+      type: "configuration"
+      data-sets:
+        plot-vpp-throughput-latency:
+          csit-vpp-perf-1710-all:
+          - 11
+          - 12
+          - 13
+          - 14
+          - 15
+          - 16
+          - 17
+          - 18
+          - 19
+          - 20
+        vpp-perf-results:
+          csit-vpp-perf-1710-all:
+          - 20
+          - 23
+      plot-layouts:
+        plot-throughput:
+          xaxis:
+            autorange: True
+            autotick: False
+            fixedrange: False
+            gridcolor: "rgb(238, 238, 238)"
+            linecolor: "rgb(238, 238, 238)"
+            linewidth: 1
+            showgrid: True
+            showline: True
+            showticklabels: True
+            tickcolor: "rgb(238, 238, 238)"
+            tickmode: "linear"
+            title: "Indexed Test Cases"
+            zeroline: False
+          yaxis:
+            gridcolor: "rgb(238, 238, 238)'"
+            hoverformat: ".4s"
+            linecolor: "rgb(238, 238, 238)"
+            linewidth: 1
+            range: []
+            showgrid: True
+            showline: True
+            showticklabels: True
+            tickcolor: "rgb(238, 238, 238)"
+            title: "Packets Per Second [pps]"
+            zeroline: False
+          boxmode: "group"
+          boxgroupgap: 0.5
+          autosize: False
+          margin:
+            t: 50
+            b: 20
+            l: 50
+            r: 20
+          showlegend: True
+          legend:
+            orientation: "h"
+          width: 700
+          height: 1000
+
+The definitions from this sections are used in the elements, e.g.:
+
+::
+
+    -
+      type: "plot"
+      title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+      algorithm: "plot_performance_box"
+      output-file-type: ".html"
+      output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel1-ndrdisc"
+      data:
+        "plot-vpp-throughput-latency"
+      filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "throughput"
+      - "parent"
+      traces:
+        hoverinfo: "x+y"
+        boxpoints: "outliers"
+        whiskerwidth: 0
+      layout:
+        title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+        layout:
+          "plot-throughput"
+
+
+Section: Debug mode
+'''''''''''''''''''
+
+This section is optional as it configures the debug mode. It is used if one
+does not want to download input data files and use local files instead.
+
+If the debug mode is configured, the "input" section is ignored.
+
+This section has the following parts:
+
+ - type: "debug" - says that this is the section "debug".
+ - general:
+
+   - input-format - xml or zip.
+   - extract - if "zip" is defined as the input format, this file is extracted
+     from the zip file, otherwise this parameter is ignored.
+
+ - builds - list of builds from which the data is used. Must include a job
+   name as a key and then a list of builds and their output files.
+
+The structure of the section "Debug" is as follows (example):
+
+::
+
+    -
+      type: "debug"
+      general:
+        input-format: "zip"  # zip or xml
+        extract: "robot-plugin/output.xml"  # Only for zip
+      builds:
+        # The files must be in the directory DIR[WORKING,DATA]
+        csit-dpdk-perf-1707-all:
+        -
+          build: 10
+          file: "csit-dpdk-perf-1707-all__10.xml"
+        -
+          build: 9
+          file: "csit-dpdk-perf-1707-all__9.xml"
+        csit-nsh_sfc-verify-func-1707-ubuntu1604-virl:
+        -
+          build: 2
+          file: "csit-nsh_sfc-verify-func-1707-ubuntu1604-virl-2.xml"
+        csit-vpp-functional-1707-ubuntu1604-virl:
+        -
+          build: lastSuccessfulBuild
+          file: "csit-vpp-functional-1707-ubuntu1604-virl-lastSuccessfulBuild.xml"
+        hc2vpp-csit-integration-1707-ubuntu1604:
+        -
+          build: lastSuccessfulBuild
+          file: "hc2vpp-csit-integration-1707-ubuntu1604-lastSuccessfulBuild.xml"
+        csit-vpp-perf-1707-all:
+        -
+          build: 16
+          file: "csit-vpp-perf-1707-all__16__output.xml"
+        -
+          build: 17
+          file: "csit-vpp-perf-1707-all__17__output.xml"
+
+
+Section: Static
+'''''''''''''''
+
+This section defines the static content which is stored in git and will be used
+as a source to generate the report.
+
+This section has these parts:
+
+ - type: "static" - says that this section is the "static".
+ - src-path - path to the static content.
+ - dst-path - destination path where the static content is copied and then
+   processed.
+
+::
+    -
+      type: "static"
+      src-path: "{DIR[RST]}"
+      dst-path: "{DIR[WORKING,SRC]}"
+
+
+Section: Input
+''''''''''''''
+
+This section defines the data used to generate elements. It is mandatory
+if the debug mode is not used.
+
+This section has the following parts:
+
+ - type: "input" - says that this section is the "input".
+ - general - parameters common to all builds:
+
+   - file-name: file to be downloaded.
+   - file-format: format of the downloaded file, ".zip" or ".xml" are supported.
+   - download-path: path to be added to url pointing to the file, e.g.:
+     "{job}/{build}/robot/report/*zip*/{filename}"; {job}, {build} and
+     {filename} are replaced by proper values defined in this section.
+   - extract: file to be extracted from downloaded zip file, e.g.: "output.xml";
+     if xml file is downloaded, this parameter is ignored.
+
+ - builds - list of jobs (keys) and numbers of builds which output data will be
+   downloaded.
+
+The structure of the section "Input" is as follows (example from 17.07 report):
+
+::
+
+    -
+      type: "input"  # Ignored in debug mode
+      general:
+        file-name: "robot-plugin.zip"
+        file-format: ".zip"
+        download-path: "{job}/{build}/robot/report/*zip*/{filename}"
+        extract: "robot-plugin/output.xml"
+      builds:
+        csit-vpp-perf-1707-all:
+        - 9
+        - 10
+        - 13
+        - 14
+        - 15
+        - 16
+        - 17
+        - 18
+        - 19
+        - 21
+        - 22
+        csit-dpdk-perf-1707-all:
+        - 1
+        - 2
+        - 3
+        - 4
+        - 5
+        - 6
+        - 7
+        - 8
+        - 9
+        - 10
+        csit-vpp-functional-1707-ubuntu1604-virl:
+        - lastSuccessfulBuild
+        hc2vpp-csit-perf-master-ubuntu1604:
+        - 8
+        - 9
+        hc2vpp-csit-integration-1707-ubuntu1604:
+        - lastSuccessfulBuild
+        csit-nsh_sfc-verify-func-1707-ubuntu1604-virl:
+        - 2
+
+
+Section: Output
+'''''''''''''''
+
+This section specifies which format(s) will be generated (html, pdf) and which
+versions will be generated for each format.
+
+This section has the following parts:
+
+ - type: "output" - says that this section is the "output".
+ - format: html or pdf.
+ - version: defined for each format separately.
+
+The structure of the section "Output" is as follows (example):
+
+::
+
+    -
+      type: "output"
+      format:
+        html:
+        - full
+        pdf:
+        - full
+        - minimal
+
+TODO: define the names of versions
+
+
+Content of "minimal" version
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+TODO: define the name and content of this version
+
+
+Section: Table
+''''''''''''''
+
+This section defines a table to be generated. There can be 0 or more "table"
+sections.
+
+This section has the following parts:
+
+ - type: "table" - says that this section defines a table.
+ - title: Title of the table.
+ - algorithm: Algorithm which is used to generate the table. The other
+   parameters in this section must provide all information needed by the used
+   algorithm.
+ - template: (optional) a .csv file used as a template while generating the
+   table.
+ - output-file-ext: extension of the output file.
+ - output-file: file which the table will be written to.
+ - columns: specification of table columns:
+
+   - title: The title used in the table header.
+   - data: Specification of the data, it has two parts - command and arguments:
+
+     - command:
+
+       - template - take the data from template, arguments:
+
+         - number of column in the template.
+
+       - data - take the data from the input data, arguments:
+
+         - jobs and builds which data will be used.
+
+       - operation - performs an operation with the data already in the table,
+         arguments:
+
+         - operation to be done, e.g.: mean, stdev, relative_change (compute
+           the relative change between two columns) and display number of data
+           samples ~= number of test jobs. The operations are implemented in the
+           utils.py
+           TODO: Move from utils,py to e.g. operations.py
+         - numbers of columns which data will be used (optional).
+
+ - data: Specify the jobs and builds which data is used to generate the table.
+ - filter: filter based on tags applied on the input data, if "template" is
+   used, filtering is based on the template.
+ - parameters: Only these parameters will be put to the output data structure.
+
+The structure of the section "Table" is as follows (example of
+"table_performance_improvements"):
+
+::
+
+    -
+      type: "table"
+      title: "Performance improvements"
+      algorithm: "table_performance_improvements"
+      template: "{DIR[DTR,PERF,VPP,IMPRV]}/tmpl_performance_improvements.csv"
+      output-file-ext: ".csv"
+      output-file: "{DIR[DTR,PERF,VPP,IMPRV]}/performance_improvements"
+      columns:
+      -
+        title: "VPP Functionality"
+        data: "template 1"
+      -
+        title: "Test Name"
+        data: "template 2"
+      -
+        title: "VPP-16.09 mean [Mpps]"
+        data: "template 3"
+      -
+        title: "VPP-17.01 mean [Mpps]"
+        data: "template 4"
+      -
+        title: "VPP-17.04 mean [Mpps]"
+        data: "template 5"
+      -
+        title: "VPP-17.07 mean [Mpps]"
+        data: "data csit-vpp-perf-1707-all mean"
+      -
+        title: "VPP-17.07 stdev [Mpps]"
+        data: "data csit-vpp-perf-1707-all stdev"
+      -
+        title: "17.04 to 17.07 change [%]"
+        data: "operation relative_change 5 4"
+      data:
+        csit-vpp-perf-1707-all:
+        - 9
+        - 10
+        - 13
+        - 14
+        - 15
+        - 16
+        - 17
+        - 18
+        - 19
+        - 21
+      filter: "template"
+      parameters:
+      - "throughput"
+
+Example of "table_details" which generates "Detailed Test Results - VPP
+Performance Results":
+
+::
+
+    -
+      type: "table"
+      title: "Detailed Test Results - VPP Performance Results"
+      algorithm: "table_details"
+      output-file-ext: ".csv"
+      output-file: "{DIR[WORKING]}/vpp_performance_results"
+      columns:
+      -
+        title: "Name"
+        data: "data test_name"
+      -
+        title: "Documentation"
+        data: "data test_documentation"
+      -
+        title: "Status"
+        data: "data test_msg"
+      data:
+        csit-vpp-perf-1707-all:
+        - 17
+      filter: "all"
+      parameters:
+      - "parent"
+      - "doc"
+      - "msg"
+
+Example of "table_details" which generates "Test configuration - VPP Performance
+Test Configs":
+
+::
+
+    -
+      type: "table"
+      title: "Test configuration - VPP Performance Test Configs"
+      algorithm: "table_details"
+      output-file-ext: ".csv"
+      output-file: "{DIR[WORKING]}/vpp_test_configuration"
+      columns:
+      -
+        title: "Name"
+        data: "data name"
+      -
+        title: "VPP API Test (VAT) Commands History - Commands Used Per Test Case"
+        data: "data show-run"
+      data:
+        csit-vpp-perf-1707-all:
+        - 17
+      filter: "all"
+      parameters:
+      - "parent"
+      - "name"
+      - "show-run"
+
+
+Section: Plot
+'''''''''''''
+
+This section defines a plot to be generated. There can be 0 or more "plot"
+sections.
+
+This section has these parts:
+
+ - type: "plot" - says that this section defines a plot.
+ - title: Plot title used in the logs. Title which is displayed is in the
+   section "layout".
+ - output-file-type: format of the output file.
+ - output-file: file which the plot will be written to.
+ - algorithm: Algorithm used to generate the plot. The other parameters in this
+   section must provide all information needed by plot.ly to generate the plot.
+   For example:
+
+   - traces
+   - layout
+
+   - These parameters are transparently passed to plot.ly.
+
+ - data: Specify the jobs and numbers of builds which data is used to generate
+   the plot.
+ - filter: filter applied on the input data.
+ - parameters: Only these parameters will be put to the output data structure.
+
+The structure of the section "Plot" is as follows (example of a plot showing
+throughput in a chart box-with-whiskers):
+
+::
+
+    -
+      type: "plot"
+      title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+      algorithm: "plot_performance_box"
+      output-file-type: ".html"
+      output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel1-ndrdisc"
+      data:
+        csit-vpp-perf-1707-all:
+        - 9
+        - 10
+        - 13
+        - 14
+        - 15
+        - 16
+        - 17
+        - 18
+        - 19
+        - 21
+      # Keep this formatting, the filter is enclosed with " (quotation mark) and
+      # each tag is enclosed with ' (apostrophe).
+      filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "throughput"
+      - "parent"
+      traces:
+        hoverinfo: "x+y"
+        boxpoints: "outliers"
+        whiskerwidth: 0
+      layout:
+        title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+        xaxis:
+          autorange: True
+          autotick: False
+          fixedrange: False
+          gridcolor: "rgb(238, 238, 238)"
+          linecolor: "rgb(238, 238, 238)"
+          linewidth: 1
+          showgrid: True
+          showline: True
+          showticklabels: True
+          tickcolor: "rgb(238, 238, 238)"
+          tickmode: "linear"
+          title: "Indexed Test Cases"
+          zeroline: False
+        yaxis:
+          gridcolor: "rgb(238, 238, 238)'"
+          hoverformat: ".4s"
+          linecolor: "rgb(238, 238, 238)"
+          linewidth: 1
+          range: []
+          showgrid: True
+          showline: True
+          showticklabels: True
+          tickcolor: "rgb(238, 238, 238)"
+          title: "Packets Per Second [pps]"
+          zeroline: False
+        boxmode: "group"
+        boxgroupgap: 0.5
+        autosize: False
+        margin:
+          t: 50
+          b: 20
+          l: 50
+          r: 20
+        showlegend: True
+        legend:
+          orientation: "h"
+        width: 700
+        height: 1000
+
+The structure of the section "Plot" is as follows (example of a plot showing
+latency in a box chart):
+
+::
+
+    -
+      type: "plot"
+      title: "VPP Latency 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+      algorithm: "plot_latency_box"
+      output-file-type: ".html"
+      output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel1-ndrdisc-lat50"
+      data:
+        csit-vpp-perf-1707-all:
+        - 9
+        - 10
+        - 13
+        - 14
+        - 15
+        - 16
+        - 17
+        - 18
+        - 19
+        - 21
+      filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "latency"
+      - "parent"
+      traces:
+        boxmean: False
+      layout:
+        title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+        xaxis:
+          autorange: True
+          autotick: False
+          fixedrange: False
+          gridcolor: "rgb(238, 238, 238)"
+          linecolor: "rgb(238, 238, 238)"
+          linewidth: 1
+          showgrid: True
+          showline: True
+          showticklabels: True
+          tickcolor: "rgb(238, 238, 238)"
+          tickmode: "linear"
+          title: "Indexed Test Cases"
+          zeroline: False
+        yaxis:
+          gridcolor: "rgb(238, 238, 238)'"
+          hoverformat: ""
+          linecolor: "rgb(238, 238, 238)"
+          linewidth: 1
+          range: []
+          showgrid: True
+          showline: True
+          showticklabels: True
+          tickcolor: "rgb(238, 238, 238)"
+          title: "Latency min/avg/max [uSec]"
+          zeroline: False
+        boxmode: "group"
+        boxgroupgap: 0.5
+        autosize: False
+        margin:
+          t: 50
+          b: 20
+          l: 50
+          r: 20
+        showlegend: True
+        legend:
+          orientation: "h"
+        width: 700
+        height: 1000
+
+The structure of the section "Plot" is as follows (example of a plot showing
+VPP HTTP server performance in a box chart with pre-defined data
+"plot-vpp-httlp-server-performance" set and  plot layout "plot-cps"):
+
+::
+
+    -
+      type: "plot"
+      title: "VPP HTTP Server Performance"
+      algorithm: "plot_http_server_performance_box"
+      output-file-type: ".html"
+      output-file: "{DIR[STATIC,VPP]}/http-server-performance-cps"
+      data:
+        "plot-vpp-httlp-server-performance"
+      # Keep this formatting, the filter is enclosed with " (quotation mark) and
+      # each tag is enclosed with ' (apostrophe).
+      filter: "'HTTP' and 'TCP_CPS'"
+      parameters:
+      - "result"
+      - "name"
+      traces:
+        hoverinfo: "x+y"
+        boxpoints: "outliers"
+        whiskerwidth: 0
+      layout:
+        title: "VPP HTTP Server Performance"
+        layout:
+          "plot-cps"
+
+
+Section: file
+'''''''''''''
+
+This section defines a file to be generated. There can be 0 or more "file"
+sections.
+
+This section has the following parts:
+
+ - type: "file" - says that this section defines a file.
+ - title: Title of the table.
+ - algorithm: Algorithm which is used to generate the file. The other
+   parameters in this section must provide all information needed by the used
+   algorithm.
+ - output-file-ext: extension of the output file.
+ - output-file: file which the file will be written to.
+ - file-header: The header of the generated .rst file.
+ - dir-tables: The directory with the tables.
+ - data: Specify the jobs and builds which data is used to generate the table.
+ - filter: filter based on tags applied on the input data, if "all" is
+   used, no filtering is done.
+ - parameters: Only these parameters will be put to the output data structure.
+ - chapters: the hierarchy of chapters in the generated file.
+ - start-level: the level of the the top-level chapter.
+
+The structure of the section "file" is as follows (example):
+
+::
+
+    -
+      type: "file"
+      title: "VPP Performance Results"
+      algorithm: "file_test_results"
+      output-file-ext: ".rst"
+      output-file: "{DIR[DTR,PERF,VPP]}/vpp_performance_results"
+      file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+      dir-tables: "{DIR[DTR,PERF,VPP]}"
+      data:
+        csit-vpp-perf-1707-all:
+        - 22
+      filter: "all"
+      parameters:
+      - "name"
+      - "doc"
+      - "level"
+      data-start-level: 2  # 0, 1, 2, ...
+      chapters-start-level: 2  # 0, 1, 2, ...
+
+
+Static content
+``````````````
+
+ - Manually created / edited files.
+ - .rst files, static .csv files, static pictures (.svg), ...
+ - Stored in CSIT git repository.
+
+No more details about the static content in this document.
+
+
+Data to process
+```````````````
+
+The PAL processes tests results and other information produced by Jenkins jobs.
+The data are now stored as robot results in Jenkins (TODO: store the data in
+nexus) either as .zip and / or .xml files.
+
+
+Data processing
+---------------
+
+As the first step, the data are downloaded and stored locally (typically on a
+Jenkins slave). If .zip files are used, the given .xml files are extracted for
+further processing.
+
+Parsing of the .xml files is performed by a class derived from
+"robot.api.ResultVisitor", only necessary methods are overridden. All and only
+necessary data is extracted from .xml file and stored in a structured form.
+
+The parsed data are stored as the multi-indexed pandas.Series data type. Its
+structure is as follows:
+
+::
+
+    <job name>
+      <build>
+        <metadata>
+        <suites>
+        <tests>
+
+"job name", "build", "metadata", "suites", "tests" are indexes to access the
+data. For example:
+
+::
+
+    data =
+
+    job 1 name:
+      build 1:
+        metadata: metadata
+        suites: suites
+        tests: tests
+      ...
+      build N:
+        metadata: metadata
+        suites: suites
+        tests: tests
+    ...
+    job M name:
+      build 1:
+        metadata: metadata
+        suites: suites
+        tests: tests
+      ...
+      build N:
+        metadata: metadata
+        suites: suites
+        tests: tests
+
+Using indexes data["job 1 name"]["build 1"]["tests"] (e.g.:
+data["csit-vpp-perf-1704-all"]["17"]["tests"]) we get a list of all tests with
+all tests data.
+
+Data will not be accessible directly using indexes, but using getters and
+filters.
+
+**Structure of metadata:**
+
+::
+
+    "metadata": {
+        "version": "VPP version",
+        "job": "Jenkins job name"
+        "build": "Information about the build"
+    },
+
+**Structure of suites:**
+
+::
+
+    "suites": {
+        "Suite name 1": {
+            "doc": "Suite 1 documentation"
+            "parent": "Suite 1 parent"
+        }
+        "Suite name N": {
+            "doc": "Suite N documentation"
+            "parent": "Suite N parent"
+        }
+
+**Structure of tests:**
+
+Performance tests:
+
+::
+
+    "tests": {
+        "ID": {
+            "name": "Test name",
+            "parent": "Name of the parent of the test",
+            "doc": "Test documentation"
+            "msg": "Test message"
+            "tags": ["tag 1", "tag 2", "tag n"],
+            "type": "PDR" | "NDR",
+            "throughput": {
+                "value": int,
+                "unit": "pps" | "bps" | "percentage"
+            },
+            "latency": {
+                "direction1": {
+                    "100": {
+                        "min": int,
+                        "avg": int,
+                        "max": int
+                    },
+                    "50": {  # Only for NDR
+                        "min": int,
+                        "avg": int,
+                        "max": int
+                    },
+                    "10": {  # Only for NDR
+                        "min": int,
+                        "avg": int,
+                        "max": int
+                    }
+                },
+                "direction2": {
+                    "100": {
+                        "min": int,
+                        "avg": int,
+                        "max": int
+                    },
+                    "50": {  # Only for NDR
+                        "min": int,
+                        "avg": int,
+                        "max": int
+                    },
+                    "10": {  # Only for NDR
+                        "min": int,
+                        "avg": int,
+                        "max": int
+                    }
+                }
+            },
+            "lossTolerance": "lossTolerance"  # Only for PDR
+            "vat-history": "DUT1 and DUT2 VAT History"
+            },
+            "show-run": "Show Run"
+        },
+        "ID" {
+            # next test
+        }
+
+Functional tests:
+
+::
+
+    "tests": {
+        "ID": {
+            "name": "Test name",
+            "parent": "Name of the parent of the test",
+            "doc": "Test documentation"
+            "msg": "Test message"
+            "tags": ["tag 1", "tag 2", "tag n"],
+            "vat-history": "DUT1 and DUT2 VAT History"
+            "show-run": "Show Run"
+            "status": "PASS" | "FAIL"
+        },
+        "ID" {
+            # next test
+        }
+    }
+
+Note: ID is the lowercase full path to the test.
+
+
+Data filtering
+``````````````
+
+The first step when generating an element is getting the data needed to
+construct the element. The data are filtered from the processed input data.
+
+The data filtering is based on:
+
+ - job name(s).
+ - build number(s).
+ - tag(s).
+ - required data - only this data is included in the output.
+
+WARNING: The filtering is based on tags, so be careful with tagging.
+
+For example, the element which specification includes:
+
+::
+
+    data:
+      csit-vpp-perf-1707-all:
+      - 9
+      - 10
+      - 13
+      - 14
+      - 15
+      - 16
+      - 17
+      - 18
+      - 19
+      - 21
+    filter:
+      - "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+
+will be constructed using data from the job "csit-vpp-perf-1707-all", for all
+listed builds and the tests with the list of tags matching the filter
+conditions.
+
+The output data structure for filtered test data is:
+
+::
+
+    - job 1
+      - build 1
+        - test 1
+          - parameter 1
+          - parameter 2
+          ...
+          - parameter n
+        ...
+        - test n
+        ...
+      ...
+      - build n
+    ...
+    - job n
+
+
+Data analytics
+``````````````
+
+Data analytics part implements:
+
+ - methods to compute statistical data from the filtered input data.
+ - trending.
+
+Throughput Speedup Analysis - Multi-Core with Multi-Threading
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Throughput Speedup Analysis (TSA) calculates throughput speedup ratios
+for tested 1-, 2- and 4-core multi-threaded VPP configurations using the
+following formula:
+
+::
+
+                                N_core_throughput
+    N_core_throughput_speedup = -----------------
+                                1_core_throughput
+
+Multi-core throughput speedup ratios are plotted in grouped bar graphs
+for throughput tests with 64B/78B frame size, with number of cores on
+X-axis and speedup ratio on Y-axis.
+
+For better comparison multiple test results' data sets are plotted per
+each graph:
+
+    - graph type: grouped bars;
+    - graph X-axis: (testcase index, number of cores);
+    - graph Y-axis: speedup factor.
+
+Subset of existing performance tests is covered by TSA graphs.
+
+**Model for TSA:**
+
+::
+
+    -
+      type: "plot"
+      title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+      algorithm: "plot_throughput_speedup_analysis"
+      output-file-type: ".html"
+      output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-l2-tsa-ndrdisc"
+      data:
+        "plot-throughput-speedup-analysis"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'BASE' and 'NDRDISC' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "throughput"
+      - "parent"
+      - "tags"
+      layout:
+        title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+        layout:
+          "plot-throughput-speedup-analysis"
+
+
+Comparison of results from two sets of the same test executions
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+This algorithm enables comparison of results coming from two sets of the
+same test executions. It is used to quantify performance changes across
+all tests after test environment changes e.g. Operating System
+upgrades/patches, Hardware changes.
+
+It is assumed that each set of test executions includes multiple runs
+of the same tests, 10 or more, to verify test results repeatibility and
+to yield statistically meaningful results data.
+
+Comparison results are presented in a table with a specified number of
+the best and the worst relative changes between the two sets. Following table
+columns are defined:
+
+    - name of the test;
+    - throughput mean values of the reference set;
+    - throughput standard deviation  of the reference set;
+    - throughput mean values of the set to compare;
+    - throughput standard deviation  of the set to compare;
+    - relative change of the mean values.
+
+**The model**
+
+The model specifies:
+
+    - type: "table" - means this section defines a table.
+    - title: Title of the table.
+    - algorithm: Algorithm which is used to generate the table. The other
+      parameters in this section must provide all information needed by the used
+      algorithm.
+    - output-file-ext: Extension of the output file.
+    - output-file: File which the table will be written to.
+    - reference - the builds which are used as the reference for comparison.
+    - compare - the builds which are compared to the reference.
+    - data: Specify the sources, jobs and builds, providing data for generating
+      the table.
+    - filter: Filter based on tags applied on the input data, if "template" is
+      used, filtering is based on the template.
+    - parameters: Only these parameters will be put to the output data
+      structure.
+    - nr-of-tests-shown: Number of the best and the worst tests presented in the
+      table. Use 0 (zero) to present all tests.
+
+*Example:*
+
+::
+
+    -
+      type: "table"
+      title: "Performance comparison"
+      algorithm: "table_performance_comparison"
+      output-file-ext: ".csv"
+      output-file: "{DIR[DTR,PERF,VPP,IMPRV]}/vpp_performance_comparison"
+      reference:
+        title: "csit-vpp-perf-1801-all - 1"
+        data:
+          csit-vpp-perf-1801-all:
+          - 1
+          - 2
+      compare:
+        title: "csit-vpp-perf-1801-all - 2"
+        data:
+          csit-vpp-perf-1801-all:
+          - 1
+          - 2
+      data:
+        "vpp-perf-comparison"
+      filter: "all"
+      parameters:
+      - "name"
+      - "parent"
+      - "throughput"
+      nr-of-tests-shown: 20
+
+
+Advanced data analytics
+```````````````````````
+
+In the future advanced data analytics (ADA) will be added to analyze the
+telemetry data collected from SUT telemetry sources and correlate it to
+performance test results.
+
+:TODO:
+
+    - describe the concept of ADA.
+    - add specification.
+
+
+Data presentation
+-----------------
+
+Generates the plots and tables according to the report models per
+specification file. The elements are generated using algorithms and data
+specified in their models.
+
+
+Tables
+``````
+
+ - tables are generated by algorithms implemented in PAL, the model includes the
+   algorithm and all necessary information.
+ - output format: csv
+ - generated tables are stored in specified directories and linked to .rst
+   files.
+
+
+Plots
+`````
+
+ - `plot.ly <https://plot.ly/>`_ is currently used to generate plots, the model
+   includes the type of plot and all the necessary information to render it.
+ - output format: html.
+ - generated plots are stored in specified directories and linked to .rst files.
+
+
+Report generation
+-----------------
+
+Report is generated using Sphinx and Read_the_Docs template. PAL generates html
+and pdf formats. It is possible to define the content of the report by
+specifying the version (TODO: define the names and content of versions).
+
+
+The process
+```````````
+
+1. Read the specification.
+2. Read the input data.
+3. Process the input data.
+4. For element (plot, table, file) defined in specification:
+
+   a. Get the data needed to construct the element using a filter.
+   b. Generate the element.
+   c. Store the element.
+
+5. Generate the report.
+6. Store the report (Nexus).
+
+The process is model driven. The elements' models (tables, plots, files
+and report itself) are defined in the specification file. Script reads
+the elements' models from specification file and generates the elements.
+
+It is easy to add elements to be generated in the report. If a new type
+of an element is required, only a new algorithm needs to be implemented
+and integrated.
+
+
+Continuous Performance Measurements and Trending
+------------------------------------------------
+
+Performance analysis and trending execution sequence:
+`````````````````````````````````````````````````````
+
+CSIT PA runs performance analysis, change detection and trending using specified
+trend analysis metrics over the rolling window of last <N> sets of historical
+measurement data. PA is defined as follows:
+
+    #. PA job triggers:
+
+        #. By PT job at its completion.
+        #. Manually from Jenkins UI.
+
+    #. Download and parse archived historical data and the new data:
+
+        #. New data from latest PT job is evaluated against the rolling window
+           of <N> sets of historical data.
+        #. Download RF output.xml files and compressed archived data.
+        #. Parse out the data filtering test cases listed in PA specification
+           (part of CSIT PAL specification file).
+
+    #. Calculate trend metrics for the rolling window of <N> sets of historical data:
+
+        #. Calculate quartiles Q1, Q2, Q3.
+        #. Trim outliers using IQR.
+        #. Calculate TMA and TMSD.
+        #. Calculate normal trending range per test case based on TMA and TMSD.
+
+    #. Evaluate new test data against trend metrics:
+
+        #. If within the range of (TMA +/- 3*TMSD) => Result = Pass,
+           Reason = Normal.
+        #. If below the range => Result = Fail, Reason = Regression.
+        #. If above the range => Result = Pass, Reason = Progression.
+
+    #. Generate and publish results
+
+        #. Relay evaluation result to job result.
+        #. Generate a new set of trend analysis summary graphs and drill-down
+           graphs.
+
+            #. Summary graphs to include measured values with Normal,
+               Progression and Regression markers. MM shown in the background if
+               possible.
+            #. Drill-down graphs to include MM, TMA and TMSD.
+
+        #. Publish trend analysis graphs in html format on
+           https://docs.fd.io/csit/master/trending/.
+
+
+Parameters to specify:
+``````````````````````
+
+- job to be monitored - the Jenkins job which results are used as input data for
+  this test;
+- builds used for trending plot(s) - specified by a list of build numbers or by
+  a range of builds defined by the first and the last buld number;
+- list plots to generate:
+
+  - plot title;
+  - output file name;
+  - data for plots;
+  - tests to be displayed in the plot defined by a filter;
+  - list of parameters to extract from the data;
+  - periods (daily = 1, weekly = 5, monthly = 30);
+  - plot layout
+
+*Example:*
+
+::
+
+    -
+      type: "cpta"
+      title: "Continuous Performance Trending and Analysis"
+      algorithm: "cpta"
+      output-file-type: ".html"
+      output-file: "{DIR[STATIC,VPP]}/cpta"
+      data: "plot-performance-trending"
+      plots:
+        - title: "VPP 1T1C L2 64B Packet Throughput - {period} Trending"
+          output-file-name: "l2-1t1c-x520"
+          data: "plot-performance-trending"
+          filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+          parameters:
+          - "result"
+    #      - "name"
+          periods:
+          - 1
+          - 5
+          - 30
+          layout: "plot-cpta"
+
+        - title: "VPP 2T2C L2 64B Packet Throughput - {period} Trending"
+          output-file-name: "l2-2t2c-x520"
+          data: "plot-performance-trending"
+          filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+          parameters:
+          - "result"
+    #      - "name"
+          periods:
+          - 1
+          - 5
+          - 30
+          layout: "plot-cpta"
+
+API
+---
+
+List of modules, classes, methods and functions
+```````````````````````````````````````````````
+
+::
+
+    specification_parser.py
+
+        class Specification
+
+            Methods:
+                read_specification
+                set_input_state
+                set_input_file_name
+
+            Getters:
+                specification
+                environment
+                debug
+                is_debug
+                input
+                builds
+                output
+                tables
+                plots
+                files
+                static
+
+
+    input_data_parser.py
+
+        class InputData
+
+            Methods:
+                read_data
+                filter_data
+
+            Getters:
+                data
+                metadata
+                suites
+                tests
+
+
+    environment.py
+
+        Functions:
+            clean_environment
+
+        class Environment
+
+            Methods:
+                set_environment
+
+            Getters:
+                environment
+
+
+    input_data_files.py
+
+        Functions:
+            download_data_files
+            unzip_files
+
+
+    generator_tables.py
+
+        Functions:
+            generate_tables
+
+        Functions implementing algorithms to generate particular types of
+        tables (called by the function "generate_tables"):
+            table_details
+            table_performance_improvements
+
+
+    generator_plots.py
+
+        Functions:
+            generate_plots
+
+        Functions implementing algorithms to generate particular types of
+        plots (called by the function "generate_plots"):
+            plot_performance_box
+            plot_latency_box
+
+
+    generator_files.py
+
+        Functions:
+            generate_files
+
+        Functions implementing algorithms to generate particular types of
+        files (called by the function "generate_files"):
+            file_test_results
+
+
+    report.py
+
+        Functions:
+            generate_report
+
+        Functions implementing algorithms to generate particular types of
+        report (called by the function "generate_report"):
+            generate_html_report
+            generate_pdf_report
+
+        Other functions called by the function "generate_report":
+            archive_input_data
+            archive_report
+
+
+PAL functional diagram
+``````````````````````
+
+.. only:: latex
+
+    .. raw:: latex
+
+        \begin{figure}[H]
+        \centering
+            \includesvg[width=0.90\textwidth]{../_tmp/src/csit_framework_documentation/pal_func_diagram}
+            \label{fig:pal_func_diagram}
+        \end{figure}
+
+.. only:: html
+
+    .. figure:: pal_func_diagram.svg
+        :alt: PAL functional diagram
+        :align: center
+
+
+How to add an element
+`````````````````````
+
+Element can be added by adding it's model to the specification file. If
+the element is to be generated by an existing algorithm, only it's
+parameters must be set.
+
+If a brand new type of element needs to be added, also the algorithm
+must be implemented. Element generation algorithms are implemented in
+the files with names starting with "generator" prefix. The name of the
+function implementing the algorithm and the name of algorithm in the
+specification file have to be the same.
diff --git a/resources/tools/presentation/new/environment.py b/resources/tools/presentation/new/environment.py
new file mode 100644 (file)
index 0000000..a2fa9a0
--- /dev/null
@@ -0,0 +1,128 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Environment
+
+Setting of the environment according to the specification specified in the
+specification YAML file.
+"""
+
+import os
+import shutil
+import logging
+
+from errors import PresentationError
+
+
+class Environment(object):
+    """Setting of the environment:
+    - set environment variables,
+    - create directories.
+    """
+
+    def __init__(self, env, force=False):
+        """Initialization.
+
+        :param env: Environment specification.
+        :param force: If True, remove old build(s) if present.
+        :type env: dict
+        :type force: bool
+        """
+
+        self._env = env
+        self._force = force
+
+    @property
+    def environment(self):
+        """Getter.
+
+        :returns: Environment settings.
+        :rtype: dict
+        """
+        return self._env
+
+    def _make_dirs(self):
+        """Create the directories specified in the 'make-dirs' part of
+        'environment' section in the specification file.
+
+        :raises: PresentationError if it is not possible to remove or create a
+        directory.
+        """
+
+        if self._force:
+            logging.info("Removing old build(s) ...")
+            for directory in self._env["build-dirs"]:
+                dir_to_remove = self._env["paths"][directory]
+                if os.path.isdir(dir_to_remove):
+                    try:
+                        shutil.rmtree(dir_to_remove)
+                        logging.info("  Removed: {}".format(dir_to_remove))
+                    except OSError:
+                        raise PresentationError("Cannot remove the directory "
+                                                "'{}'".format(dir_to_remove))
+            logging.info("Done.")
+
+        logging.info("Making directories ...")
+
+        for directory in self._env["make-dirs"]:
+            dir_to_make = self._env["paths"][directory]
+            try:
+                if os.path.isdir(dir_to_make):
+                    logging.warning("The directory '{}' exists, skipping.".
+                                    format(dir_to_make))
+                else:
+                    os.makedirs(dir_to_make)
+                    logging.info("  Created: {}".format(dir_to_make))
+            except OSError:
+                raise PresentationError("Cannot make the directory '{}'".
+                                        format(dir_to_make))
+
+        logging.info("Done.")
+
+    def set_environment(self):
+        """Set the environment.
+        """
+
+        self._make_dirs()
+
+
+def clean_environment(env):
+    """Clean the environment.
+
+    :param env: Environment specification.
+    :type env: dict
+    :raises: PresentationError if it is not possible to remove a directory.
+    """
+
+    logging.info("Cleaning the environment ...")
+
+    if not env["remove-dirs"]:  # None or empty
+        logging.info("  No directories to remove.")
+        return
+
+    for directory in env["remove-dirs"]:
+        dir_to_remove = env["paths"][directory]
+        logging.info("  Removing the working directory {} ...".
+                     format(dir_to_remove))
+        if os.path.isdir(dir_to_remove):
+            try:
+                shutil.rmtree(dir_to_remove)
+            except OSError as err:
+                logging.warning("Cannot remove the directory '{}'".
+                                format(dir_to_remove))
+                logging.debug(str(err))
+        else:
+            logging.warning("The directory '{}' does not exist.".
+                            format(dir_to_remove))
+
+    logging.info("Done.")
diff --git a/resources/tools/presentation/new/errors.py b/resources/tools/presentation/new/errors.py
new file mode 100644 (file)
index 0000000..0d8d5b9
--- /dev/null
@@ -0,0 +1,78 @@
+# Copyright (c) 2017 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Implementation of exceptions used in the Presentation and analytics layer.
+"""
+
+import sys
+import logging
+
+
+class PresentationError(Exception):
+    """Exception(s) raised by the presentation module.
+
+    When raising this exception, put this information to the message in this
+    order:
+     - short description of the encountered problem (parameter msg),
+     - relevant messages if there are any collected, e.g., from caught
+       exception (optional parameter details),
+     - relevant data if there are any collected (optional parameter details).
+    """
+
+    log_exception = {"DEBUG": logging.debug,
+                     "INFO": logging.info,
+                     "WARNING": logging.warning,
+                     "ERROR": logging.error,
+                     "CRITICAL": logging.critical}
+
+    def __init__(self, msg, details='', level="CRITICAL"):
+        """Sets the exception message and the level.
+
+        :param msg: Short description of the encountered problem.
+        :param details: Relevant messages if there are any collected, e.g.,
+        from caught exception (optional parameter details), or relevant data if
+        there are any collected (optional parameter details).
+        :param level: Level of the error, possible choices are: "DEBUG", "INFO",
+        "WARNING", "ERROR" and "CRITICAL".
+        :type msg: str
+        :type details: str
+        :type level: str
+        """
+
+        super(PresentationError, self).__init__()
+        self._msg = msg
+        self._details = details
+        self._level = level
+
+        try:
+            self.log_exception[self._level](self._msg)
+            if self._details:
+                self.log_exception[self._level](self._details)
+        except KeyError:
+            print("Wrong log level.")
+            sys.exit(1)
+
+    def __repr__(self):
+        return repr(self._msg)
+
+    def __str__(self):
+        return str(self._msg)
+
+    @property
+    def level(self):
+        """Getter - logging level.
+
+        :returns: Logging level.
+        :rtype: str
+        """
+        return self._level
diff --git a/resources/tools/presentation/new/fdio.svg b/resources/tools/presentation/new/fdio.svg
new file mode 100644 (file)
index 0000000..32dd070
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="630px" height="394px" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
+viewBox="0 0 94500000 59062500"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+  <style type="text/css">
+   <![CDATA[
+    .fil2 {fill:#3C4641}
+    .fil1 {fill:#3E4742}
+    .fil0 {fill:#ED3341}
+   ]]>
+  </style>
+ </defs>
+ <g id="Layer_x0020_1">
+  <path class="fil0" d="M42892251 15970973c234833,2555989 1402971,3784607 3633525,3195163 64614,-17483 464586,-605509 524121,-613778 100406,-13939 -325789,592397 -234951,589562 663981,-21026 1166839,235659 2703054,-1575788 -125567,2507794 385088,1931698 753047,3733577 -1039736,2409632 -1745415,307125 -1486485,4471622 -4668654,-2126 -6967721,-648979 -9839813,2159679 -1571417,1536924 -3134683,5759184 -3805515,8365022 -725760,124740 -1467939,37800 -1887401,510536 -484194,545619 -431747,1408050 35674,1833418 379890,345516 987289,308424 1519678,355793 800769,71348 1142151,473327 1136244,1236651 -6615,848964 -451001,1192826 -1631306,1191054 -3702746,-5316 -8320961,16538 -11713866,45596 -1026624,8741 -1698992,464822 -1746714,1344971 -50676,931416 521640,1260039 1499833,1295831 4286993,156988 8304069,-117653 12887201,141041 783996,103714 1189755,583183 1168256,1106004 -35201,855461 -707923,1099626 -1411594,1145104 -5730598,370440 -13969581,-91665 -19611231,141278 -797344,32839 -1694621,179786 -1702890,1222476 -6615,840459 589798,1177943 1399899,1281538 2377738,7088 5327556,1418 7519956,1890 2596151,591 5192775,4134 7788926,-118l-1007843 3222096 9601909 24334 3086606 -8484328c2943911,-111628 4325029,-862076 6023666,-2197361 944528,-742534 2909773,-3271590 3377903,-4985466l-6699578 -78081 1196016 -3619114c2873627,-19727 7980761,562984 10127447,-748913 1534089,-937440 4106970,-3980458 4422954,-6498411 -1731358,-38863 -8963443,287753 -9479649,-290469 -811991,-1588073 1028633,-2402426 1978121,-3132911 1388678,-1068441 1793846,-1560904 1785578,-3760746 230108,313386 87413,679809 294368,826048 502740,355320 1446913,224319 3038411,773010 1055447,363943 2115855,607399 3274661,404696 606218,-105958 570662,-121196 999692,-407413 -87767,-924683 -247472,-1025089 -709813,-1469003 1446086,654176 966617,1888937 966499,3676168 0,1623983 504512,3115429 -2599,4527259 -1397891,3892219 -3372351,4768234 -4125752,6086745l4834856 25043 -4320540 12061626c-2523504,20318 -10295893,-341499 -12094937,331459 -2551264,954568 -5105126,4809341 -5427844,6962878 3747398,-8151 17429226,243219 20078179,-244755 5769579,-1062534 6837902,-6323231 8783066,-11759344 1441716,-4029244 5291646,-11853371 -1699464,-14191183 -1193535,-382253 -1853145,-295667 -2077228,-395955 -216523,-96863 4489,-368078 -1209600,-1404861 -1196488,-1140379 -417690,-2367934 118716,-3662820 1265001,-3005691 3370461,-2733058 3370343,-6944096 -236,-4388934 2680611,-3141771 3022583,-4763627 307479,-1458135 -1345326,-645317 -2307336,-1215388 -794273,-470610 -170809,-694693 -1801524,-842940l-178251 -1084151c-406114,246999 -713003,925509 -824631,912870 -159705,-18191 -35083,-801478 -213216,-759780 -33075,7796 -73828,19845 -123323,42643 -162068,75009 -368668,285036 -609053,585900 -1161759,1454355 -3112358,5035433 -4642076,5789779 -3004746,1481878 -5309719,91074 -10962709,3162206 -2587056,1405451 -4703383,1914216 -7362259,348233z"/>
+  <path class="fil1" d="M81444116 48006591c-917831,-4596598 5486670,-5710989 6489669,-1741753 1221649,4834502 -5678387,5805017 -6489669,1741753zm1873463 -5811986c-6608858,1801052 -4202297,11851718 2867130,10021253 6433324,-1665681 4111577,-11922947 -2867130,-10021253z"/>
+  <polygon class="fil2" points="76840667,42255321 75263698,42301508 75172978,52072571 76944144,52066547 "/>
+  <path class="fil1" d="M70635206 52314137c1084388,307834 2141370,-714302 1520859,-1977413 -474863,-966853 -2784797,-983627 -2488776,930589 89421,578931 388041,882158 967916,1046824z"/>
+  <path class="fil1" d="M76175978 40542272c695402,-45006 1114037,-372448 1062889,-1137426 -18309,-275349 -120251,-539595 -294013,-710876 -609761,-601611 -1840151,-411548 -1952016,509001 -111628,919485 435173,1387733 1183140,1339301z"/>
+  <path class="fil0" d="M14086524 36346354l15995897 0c719972,0 1308943,589089 1308943,1308943l0 0c0,719854 -589089,1308943 -1308943,1308943l-15995897 0c-719736,0 -1308943,-588971 -1308943,-1308943l0 0c0,-719972 589089,-1308943 1308943,-1308943z"/>
+  <path class="fil0" d="M20652975 31242173l13718919 0c719972,0 1308943,589326 1308943,1308943l0 118c0,719618 -589208,1308943 -1308943,1308943l-13718919 0c-719736,0 -1308943,-588971 -1308943,-1308943l0 -118c0,-719972 588971,-1308943 1308943,-1308943z"/>
+  <path class="fil0" d="M6885624 46609763l3154410 0c719972,0 1308943,589208 1308943,1308943l0 0c0,719736 -589326,1308943 -1308943,1308943l-3154410 0c-719736,0 -1308943,-588971 -1308943,-1308943l0 0c0,-719972 588971,-1308943 1308943,-1308943z"/>
+ </g>
+</svg>
diff --git a/resources/tools/presentation/new/generator_CPTA.py b/resources/tools/presentation/new/generator_CPTA.py
new file mode 100644 (file)
index 0000000..1b4115f
--- /dev/null
@@ -0,0 +1,448 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Generation of Continuous Performance Trending and Analysis.
+"""
+
+import multiprocessing
+import os
+import logging
+import csv
+import prettytable
+import plotly.offline as ploff
+import plotly.graph_objs as plgo
+import plotly.exceptions as plerr
+import pandas as pd
+
+from collections import OrderedDict
+from datetime import datetime
+
+from utils import archive_input_data, execute_command,\
+    classify_anomalies, Worker
+
+
+# Command to build the html format of the report
+HTML_BUILDER = 'sphinx-build -v -c conf_cpta -a ' \
+               '-b html -E ' \
+               '-t html ' \
+               '-D version="{date}" ' \
+               '{working_dir} ' \
+               '{build_dir}/'
+
+# .css file for the html format of the report
+THEME_OVERRIDES = """/* override table width restrictions */
+.wy-nav-content {
+    max-width: 1200px !important;
+}
+"""
+
+COLORS = ["SkyBlue", "Olive", "Purple", "Coral", "Indigo", "Pink",
+          "Chocolate", "Brown", "Magenta", "Cyan", "Orange", "Black",
+          "Violet", "Blue", "Yellow"]
+
+
+def generate_cpta(spec, data):
+    """Generate all formats and versions of the Continuous Performance Trending
+    and Analysis.
+
+    :param spec: Specification read from the specification file.
+    :param data: Full data set.
+    :type spec: Specification
+    :type data: InputData
+    """
+
+    logging.info("Generating the Continuous Performance Trending and Analysis "
+                 "...")
+
+    ret_code = _generate_all_charts(spec, data)
+
+    cmd = HTML_BUILDER.format(
+        date=datetime.utcnow().strftime('%m/%d/%Y %H:%M UTC'),
+        working_dir=spec.environment["paths"]["DIR[WORKING,SRC]"],
+        build_dir=spec.environment["paths"]["DIR[BUILD,HTML]"])
+    execute_command(cmd)
+
+    with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE]"], "w") as \
+            css_file:
+        css_file.write(THEME_OVERRIDES)
+
+    with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE2]"], "w") as \
+            css_file:
+        css_file.write(THEME_OVERRIDES)
+
+    archive_input_data(spec)
+
+    logging.info("Done.")
+
+    return ret_code
+
+
+def _generate_trending_traces(in_data, build_info, moving_win_size=10,
+                              show_trend_line=True, name="", color=""):
+    """Generate the trending traces:
+     - samples,
+     - trimmed moving median (trending line)
+     - outliers, regress, progress
+
+    :param in_data: Full data set.
+    :param build_info: Information about the builds.
+    :param moving_win_size: Window size.
+    :param show_trend_line: Show moving median (trending plot).
+    :param name: Name of the plot
+    :param color: Name of the color for the plot.
+    :type in_data: OrderedDict
+    :type build_info: dict
+    :type moving_win_size: int
+    :type show_trend_line: bool
+    :type name: str
+    :type color: str
+    :returns: Generated traces (list) and the evaluated result.
+    :rtype: tuple(traces, result)
+    """
+
+    data_x = list(in_data.keys())
+    data_y = list(in_data.values())
+
+    hover_text = list()
+    xaxis = list()
+    for idx in data_x:
+        hover_text.append("vpp-ref: {0}<br>csit-ref: mrr-daily-build-{1}".
+                          format(build_info[str(idx)][1].rsplit('~', 1)[0],
+                                 idx))
+        date = build_info[str(idx)][0]
+        xaxis.append(datetime(int(date[0:4]), int(date[4:6]), int(date[6:8]),
+                              int(date[9:11]), int(date[12:])))
+
+    data_pd = pd.Series(data_y, index=xaxis)
+
+    anomaly_classification, avgs = classify_anomalies(data_pd)
+
+    anomalies = pd.Series()
+    anomalies_colors = list()
+    anomalies_avgs = list()
+    anomaly_color = {
+        "outlier": 0.0,
+        "regression": 0.33,
+        "normal": 0.66,
+        "progression": 1.0
+    }
+    if anomaly_classification:
+        for idx, item in enumerate(data_pd.items()):
+            if anomaly_classification[idx] in \
+                    ("outlier", "regression", "progression"):
+                anomalies = anomalies.append(pd.Series([item[1], ],
+                                                       index=[item[0], ]))
+                anomalies_colors.append(
+                    anomaly_color[anomaly_classification[idx]])
+                anomalies_avgs.append(avgs[idx])
+        anomalies_colors.extend([0.0, 0.33, 0.66, 1.0])
+
+    # Create traces
+
+    trace_samples = plgo.Scatter(
+        x=xaxis,
+        y=data_y,
+        mode='markers',
+        line={
+            "width": 1
+        },
+        legendgroup=name,
+        name="{name}-thput".format(name=name),
+        marker={
+            "size": 5,
+            "color": color,
+            "symbol": "circle",
+        },
+        text=hover_text,
+        hoverinfo="x+y+text+name"
+    )
+    traces = [trace_samples, ]
+
+    if show_trend_line:
+        trace_trend = plgo.Scatter(
+            x=xaxis,
+            y=avgs,
+            mode='lines',
+            line={
+                "shape": "linear",
+                "width": 1,
+                "color": color,
+            },
+            legendgroup=name,
+            name='{name}-trend'.format(name=name)
+        )
+        traces.append(trace_trend)
+
+    trace_anomalies = plgo.Scatter(
+        x=anomalies.keys(),
+        y=anomalies_avgs,
+        mode='markers',
+        hoverinfo="none",
+        showlegend=True,
+        legendgroup=name,
+        name="{name}-anomalies".format(name=name),
+        marker={
+            "size": 15,
+            "symbol": "circle-open",
+            "color": anomalies_colors,
+            "colorscale": [[0.00, "grey"],
+                           [0.25, "grey"],
+                           [0.25, "red"],
+                           [0.50, "red"],
+                           [0.50, "white"],
+                           [0.75, "white"],
+                           [0.75, "green"],
+                           [1.00, "green"]],
+            "showscale": True,
+            "line": {
+                "width": 2
+            },
+            "colorbar": {
+                "y": 0.5,
+                "len": 0.8,
+                "title": "Circles Marking Data Classification",
+                "titleside": 'right',
+                "titlefont": {
+                    "size": 14
+                },
+                "tickmode": 'array',
+                "tickvals": [0.125, 0.375, 0.625, 0.875],
+                "ticktext": ["Outlier", "Regression", "Normal", "Progression"],
+                "ticks": "",
+                "ticklen": 0,
+                "tickangle": -90,
+                "thickness": 10
+            }
+        }
+    )
+    traces.append(trace_anomalies)
+
+    return traces, anomaly_classification[-1]
+
+
+def _generate_all_charts(spec, input_data):
+    """Generate all charts specified in the specification file.
+
+    :param spec: Specification.
+    :param input_data: Full data set.
+    :type spec: Specification
+    :type input_data: InputData
+    """
+
+    def _generate_chart(_, data_q, graph):
+        """Generates the chart.
+        """
+
+        logs = list()
+
+        logging.info("  Generating the chart '{0}' ...".
+                     format(graph.get("title", "")))
+        logs.append(("INFO", "  Generating the chart '{0}' ...".
+                     format(graph.get("title", ""))))
+
+        job_name = spec.cpta["data"].keys()[0]
+
+        csv_tbl = list()
+        res = list()
+
+        # Transform the data
+        logs.append(("INFO", "    Creating the data set for the {0} '{1}'.".
+                     format(graph.get("type", ""), graph.get("title", ""))))
+        data = input_data.filter_data(graph, continue_on_error=True)
+        if data is None:
+            logging.error("No data.")
+            return
+
+        chart_data = dict()
+        for job in data:
+            for index, bld in job.items():
+                for test_name, test in bld.items():
+                    if chart_data.get(test_name, None) is None:
+                        chart_data[test_name] = OrderedDict()
+                    try:
+                        chart_data[test_name][int(index)] = \
+                            test["result"]["throughput"]
+                    except (KeyError, TypeError):
+                        pass
+
+        # Add items to the csv table:
+        for tst_name, tst_data in chart_data.items():
+            tst_lst = list()
+            for bld in builds_lst:
+                itm = tst_data.get(int(bld), '')
+                tst_lst.append(str(itm))
+            csv_tbl.append("{0},".format(tst_name) + ",".join(tst_lst) + '\n')
+        # Generate traces:
+        traces = list()
+        win_size = 14
+        index = 0
+        for test_name, test_data in chart_data.items():
+            if not test_data:
+                logs.append(("WARNING", "No data for the test '{0}'".
+                             format(test_name)))
+                continue
+            test_name = test_name.split('.')[-1]
+            trace, rslt = _generate_trending_traces(
+                test_data,
+                build_info=build_info,
+                moving_win_size=win_size,
+                name='-'.join(test_name.split('-')[3:-1]),
+                color=COLORS[index])
+            traces.extend(trace)
+            res.append(rslt)
+            index += 1
+
+        if traces:
+            # Generate the chart:
+            graph["layout"]["xaxis"]["title"] = \
+                graph["layout"]["xaxis"]["title"].format(job=job_name)
+            name_file = "{0}-{1}{2}".format(spec.cpta["output-file"],
+                                            graph["output-file-name"],
+                                            spec.cpta["output-file-type"])
+
+            logs.append(("INFO", "    Writing the file '{0}' ...".
+                         format(name_file)))
+            plpl = plgo.Figure(data=traces, layout=graph["layout"])
+            try:
+                ploff.plot(plpl, show_link=False, auto_open=False,
+                           filename=name_file)
+            except plerr.PlotlyEmptyDataError:
+                logs.append(("WARNING", "No data for the plot. Skipped."))
+
+        data_out = {
+            "csv_table": csv_tbl,
+            "results": res,
+            "logs": logs
+        }
+        data_q.put(data_out)
+
+    job_name = spec.cpta["data"].keys()[0]
+
+    builds_lst = list()
+    for build in spec.input["builds"][job_name]:
+        status = build["status"]
+        if status != "failed" and status != "not found":
+            builds_lst.append(str(build["build"]))
+
+    # Get "build ID": "date" dict:
+    build_info = OrderedDict()
+    for build in builds_lst:
+        try:
+            build_info[build] = (
+                input_data.metadata(job_name, build)["generated"][:14],
+                input_data.metadata(job_name, build)["version"]
+            )
+        except KeyError:
+            build_info[build] = ("", "")
+
+    work_queue = multiprocessing.JoinableQueue()
+    manager = multiprocessing.Manager()
+    data_queue = manager.Queue()
+    cpus = multiprocessing.cpu_count()
+
+    workers = list()
+    for cpu in range(cpus):
+        worker = Worker(work_queue,
+                        data_queue,
+                        _generate_chart)
+        worker.daemon = True
+        worker.start()
+        workers.append(worker)
+        os.system("taskset -p -c {0} {1} > /dev/null 2>&1".
+                  format(cpu, worker.pid))
+
+    for chart in spec.cpta["plots"]:
+        work_queue.put((chart, ))
+    work_queue.join()
+
+    anomaly_classifications = list()
+
+    # Create the header:
+    csv_table = list()
+    header = "Build Number:," + ",".join(builds_lst) + '\n'
+    csv_table.append(header)
+    build_dates = [x[0] for x in build_info.values()]
+    header = "Build Date:," + ",".join(build_dates) + '\n'
+    csv_table.append(header)
+    vpp_versions = [x[1] for x in build_info.values()]
+    header = "VPP Version:," + ",".join(vpp_versions) + '\n'
+    csv_table.append(header)
+
+    while not data_queue.empty():
+        result = data_queue.get()
+
+        anomaly_classifications.extend(result["results"])
+        csv_table.extend(result["csv_table"])
+
+        for item in result["logs"]:
+            if item[0] == "INFO":
+                logging.info(item[1])
+            elif item[0] == "ERROR":
+                logging.error(item[1])
+            elif item[0] == "DEBUG":
+                logging.debug(item[1])
+            elif item[0] == "CRITICAL":
+                logging.critical(item[1])
+            elif item[0] == "WARNING":
+                logging.warning(item[1])
+
+    del data_queue
+
+    # Terminate all workers
+    for worker in workers:
+        worker.terminate()
+        worker.join()
+
+    # Write the tables:
+    file_name = spec.cpta["output-file"] + "-trending"
+    with open("{0}.csv".format(file_name), 'w') as file_handler:
+        file_handler.writelines(csv_table)
+
+    txt_table = None
+    with open("{0}.csv".format(file_name), 'rb') as csv_file:
+        csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
+        line_nr = 0
+        for row in csv_content:
+            if txt_table is None:
+                txt_table = prettytable.PrettyTable(row)
+            else:
+                if line_nr > 1:
+                    for idx, item in enumerate(row):
+                        try:
+                            row[idx] = str(round(float(item) / 1000000, 2))
+                        except ValueError:
+                            pass
+                try:
+                    txt_table.add_row(row)
+                except Exception as err:
+                    logging.warning("Error occurred while generating TXT table:"
+                                    "\n{0}".format(err))
+            line_nr += 1
+        txt_table.align["Build Number:"] = "l"
+    with open("{0}.txt".format(file_name), "w") as txt_file:
+        txt_file.write(str(txt_table))
+
+    # Evaluate result:
+    if anomaly_classifications:
+        result = "PASS"
+        for classification in anomaly_classifications:
+            if classification == "regression" or classification == "outlier":
+                result = "FAIL"
+                break
+    else:
+        result = "FAIL"
+
+    logging.info("Partial results: {0}".format(anomaly_classifications))
+    logging.info("Result: {0}".format(result))
+
+    return result
diff --git a/resources/tools/presentation/new/generator_files.py b/resources/tools/presentation/new/generator_files.py
new file mode 100644 (file)
index 0000000..f8428ad
--- /dev/null
@@ -0,0 +1,177 @@
+# Copyright (c) 2017 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Algorithms to generate files.
+"""
+
+
+import logging
+
+from utils import get_files, get_rst_title_char
+
+RST_INCLUDE_TABLE = ("\n.. only:: html\n\n"
+                     "    .. csv-table::\n"
+                     "        :header-rows: 1\n"
+                     "        :widths: auto\n"
+                     "        :align: center\n"
+                     "        :file: {file_html}\n"
+                     "\n.. only:: latex\n\n"
+                     "\n  .. raw:: latex\n\n"
+                     "      \csvautolongtable{{{file_latex}}}\n\n")
+
+
+def generate_files(spec, data):
+    """Generate all files specified in the specification file.
+
+    :param spec: Specification read from the specification file.
+    :param data: Data to process.
+    :type spec: Specification
+    :type data: InputData
+    """
+
+    logging.info("Generating the files ...")
+    for file_spec in spec.files:
+        try:
+            eval(file_spec["algorithm"])(file_spec, data)
+        except NameError as err:
+            logging.error("Probably algorithm '{alg}' is not defined: {err}".
+                          format(alg=file_spec["algorithm"], err=repr(err)))
+    logging.info("Done.")
+
+
+def _tests_in_suite(suite_name, tests):
+    """Check if the suite includes tests.
+
+    :param suite_name: Name of the suite to be checked.
+    :param tests: Set of tests
+    :type suite_name: str
+    :type tests: pandas.Series
+    :returns: True if the suite includes tests.
+    :rtype: bool
+    """
+
+    for key in tests.keys():
+        if suite_name == tests[key]["parent"]:
+            return True
+    return False
+
+
+def file_test_results(file_spec, input_data):
+    """Generate the file(s) with algorithm: file_test_results specified in the
+    specification file.
+
+    :param file_spec: File to generate.
+    :param input_data: Data to process.
+    :type file_spec: pandas.Series
+    :type input_data: InputData
+    """
+
+    file_name = "{0}{1}".format(file_spec["output-file"],
+                                file_spec["output-file-ext"])
+    rst_header = file_spec["file-header"]
+
+    logging.info("  Generating the file {0} ...".format(file_name))
+
+    table_lst = get_files(file_spec["dir-tables"], ".csv", full_path=True)
+    if len(table_lst) == 0:
+        logging.error("  No tables to include in '{0}'. Skipping.".
+                      format(file_spec["dir-tables"]))
+        return None
+
+    job = file_spec["data"].keys()[0]
+    build = str(file_spec["data"][job][0])
+
+    logging.info("    Writing file '{0}'".format(file_name))
+
+    suites = input_data.suites(job, build)[file_spec["data-start-level"]:]
+    suites.sort_index(inplace=True)
+
+    with open(file_name, "w") as file_handler:
+        file_handler.write(rst_header)
+        for suite_longname, suite in suites.iteritems():
+            suite_name = suite["name"]
+            file_handler.write("\n{0}\n{1}\n".format(
+                suite_name, get_rst_title_char(
+                    suite["level"] - file_spec["data-start-level"] - 1) *
+                            len(suite_name)))
+            file_handler.write("\n{0}\n".format(
+                suite["doc"].replace('|br|', '\n\n -')))
+            if _tests_in_suite(suite_name, input_data.tests(job, build)):
+                for tbl_file in table_lst:
+                    if suite_name in tbl_file:
+                        file_handler.write(
+                            RST_INCLUDE_TABLE.format(
+                                file_latex=tbl_file,
+                                file_html=tbl_file.split("/")[-1]))
+
+    logging.info("  Done.")
+
+
+def file_merged_test_results(file_spec, input_data):
+    """Generate the file(s) with algorithm: file_merged_test_results specified
+    in the specification file.
+
+    :param file_spec: File to generate.
+    :param input_data: Data to process.
+    :type file_spec: pandas.Series
+    :type input_data: InputData
+    """
+
+    file_name = "{0}{1}".format(file_spec["output-file"],
+                                file_spec["output-file-ext"])
+    rst_header = file_spec["file-header"]
+
+    logging.info("  Generating the file {0} ...".format(file_name))
+
+    table_lst = get_files(file_spec["dir-tables"], ".csv", full_path=True)
+    if len(table_lst) == 0:
+        logging.error("  No tables to include in '{0}'. Skipping.".
+                      format(file_spec["dir-tables"]))
+        return None
+
+    logging.info("    Writing file '{0}'".format(file_name))
+
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(file_spec.get("type", ""), file_spec.get("title", "")))
+    tests = input_data.filter_data(file_spec)
+    tests = input_data.merge_data(tests)
+
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(file_spec.get("type", ""), file_spec.get("title", "")))
+    suites = input_data.filter_data(file_spec, data_set="suites")
+    suites = input_data.merge_data(suites)
+    suites.sort_index(inplace=True)
+
+    with open(file_name, "w") as file_handler:
+        file_handler.write(rst_header)
+        for suite_longname, suite in suites.iteritems():
+            if "ndrchk" in suite_longname or "pdrchk" in suite_longname:
+                continue
+            if len(suite_longname.split(".")) <= file_spec["data-start-level"]:
+                continue
+            suite_name = suite["name"]
+            file_handler.write("\n{0}\n{1}\n".format(
+                suite_name, get_rst_title_char(
+                    suite["level"] - file_spec["data-start-level"] - 1) *
+                            len(suite_name)))
+            file_handler.write("\n{0}\n".format(
+                suite["doc"].replace('|br|', '\n\n -')))
+            if _tests_in_suite(suite_name, tests):
+                for tbl_file in table_lst:
+                    if suite_name in tbl_file:
+                        file_handler.write(
+                            RST_INCLUDE_TABLE.format(
+                                file_latex=tbl_file,
+                                file_html=tbl_file.split("/")[-1]))
+
+    logging.info("  Done.")
diff --git a/resources/tools/presentation/new/generator_plots.py b/resources/tools/presentation/new/generator_plots.py
new file mode 100644 (file)
index 0000000..aaee31f
--- /dev/null
@@ -0,0 +1,399 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Algorithms to generate plots.
+"""
+
+
+import logging
+import pandas as pd
+import plotly.offline as ploff
+import plotly.graph_objs as plgo
+
+from plotly.exceptions import PlotlyError
+
+from utils import mean
+
+
+def generate_plots(spec, data):
+    """Generate all plots specified in the specification file.
+
+    :param spec: Specification read from the specification file.
+    :param data: Data to process.
+    :type spec: Specification
+    :type data: InputData
+    """
+
+    logging.info("Generating the plots ...")
+    for index, plot in enumerate(spec.plots):
+        try:
+            logging.info("  Plot nr {0}:".format(index + 1))
+            eval(plot["algorithm"])(plot, data)
+        except NameError as err:
+            logging.error("Probably algorithm '{alg}' is not defined: {err}".
+                          format(alg=plot["algorithm"], err=repr(err)))
+    logging.info("Done.")
+
+
+def plot_performance_box(plot, input_data):
+    """Generate the plot(s) with algorithm: plot_performance_box
+    specified in the specification file.
+
+    :param plot: Plot to generate.
+    :param input_data: Data to process.
+    :type plot: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the plot {0} ...".
+                 format(plot.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(plot.get("type", ""), plot.get("title", "")))
+    data = input_data.filter_data(plot)
+    if data is None:
+        logging.error("No data.")
+        return
+
+    # Prepare the data for the plot
+    y_vals = dict()
+    for job in data:
+        for build in job:
+            for test in build:
+                if y_vals.get(test["parent"], None) is None:
+                    y_vals[test["parent"]] = list()
+                try:
+                    y_vals[test["parent"]].append(test["throughput"]["value"])
+                except (KeyError, TypeError):
+                    y_vals[test["parent"]].append(None)
+
+    # Add None to the lists with missing data
+    max_len = 0
+    for val in y_vals.values():
+        if len(val) > max_len:
+            max_len = len(val)
+    for key, val in y_vals.items():
+        if len(val) < max_len:
+            val.extend([None for _ in range(max_len - len(val))])
+
+    # Add plot traces
+    traces = list()
+    df = pd.DataFrame(y_vals)
+    df.head()
+    for i, col in enumerate(df.columns):
+        name = "{0}. {1}".format(i + 1, col.lower().replace('-ndrpdrdisc', ''))
+        traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
+                               y=df[col],
+                               name=name,
+                               **plot["traces"]))
+
+    try:
+        # Create plot
+        plpl = plgo.Figure(data=traces, layout=plot["layout"])
+
+        # Export Plot
+        logging.info("    Writing file '{0}{1}'.".
+                     format(plot["output-file"], plot["output-file-type"]))
+        ploff.plot(plpl,
+                   show_link=False, auto_open=False,
+                   filename='{0}{1}'.format(plot["output-file"],
+                                            plot["output-file-type"]))
+    except PlotlyError as err:
+        logging.error("   Finished with error: {}".
+                      format(str(err).replace("\n", " ")))
+        return
+
+    logging.info("  Done.")
+
+
+def plot_latency_box(plot, input_data):
+    """Generate the plot(s) with algorithm: plot_latency_box
+    specified in the specification file.
+
+    :param plot: Plot to generate.
+    :param input_data: Data to process.
+    :type plot: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the plot {0} ...".
+                 format(plot.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(plot.get("type", ""), plot.get("title", "")))
+    data = input_data.filter_data(plot)
+    if data is None:
+        logging.error("No data.")
+        return
+
+    # Prepare the data for the plot
+    y_tmp_vals = dict()
+    for job in data:
+        for build in job:
+            for test in build:
+                if y_tmp_vals.get(test["parent"], None) is None:
+                    y_tmp_vals[test["parent"]] = [
+                        list(),  # direction1, min
+                        list(),  # direction1, avg
+                        list(),  # direction1, max
+                        list(),  # direction2, min
+                        list(),  # direction2, avg
+                        list()   # direction2, max
+                    ]
+                try:
+                    y_tmp_vals[test["parent"]][0].append(
+                        test["latency"]["direction1"]["50"]["min"])
+                    y_tmp_vals[test["parent"]][1].append(
+                        test["latency"]["direction1"]["50"]["avg"])
+                    y_tmp_vals[test["parent"]][2].append(
+                        test["latency"]["direction1"]["50"]["max"])
+                    y_tmp_vals[test["parent"]][3].append(
+                        test["latency"]["direction2"]["50"]["min"])
+                    y_tmp_vals[test["parent"]][4].append(
+                        test["latency"]["direction2"]["50"]["avg"])
+                    y_tmp_vals[test["parent"]][5].append(
+                        test["latency"]["direction2"]["50"]["max"])
+                except (KeyError, TypeError):
+                    pass
+
+    y_vals = dict()
+    for key, values in y_tmp_vals.items():
+        y_vals[key] = list()
+        for val in values:
+            if val:
+                average = mean(val)
+            else:
+                average = None
+            y_vals[key].append(average)
+            y_vals[key].append(average)  # Twice for plot.ly
+
+    # Add plot traces
+    traces = list()
+    try:
+        df = pd.DataFrame(y_vals)
+        df.head()
+    except ValueError as err:
+        logging.error("   Finished with error: {}".
+                      format(str(err).replace("\n", " ")))
+        return
+
+    for i, col in enumerate(df.columns):
+        name = "{0}. {1}".format(i + 1, col.lower().replace('-ndrpdrdisc', ''))
+        traces.append(plgo.Box(x=['TGint1-to-SUT1-to-SUT2-to-TGint2',
+                                  'TGint1-to-SUT1-to-SUT2-to-TGint2',
+                                  'TGint1-to-SUT1-to-SUT2-to-TGint2',
+                                  'TGint1-to-SUT1-to-SUT2-to-TGint2',
+                                  'TGint1-to-SUT1-to-SUT2-to-TGint2',
+                                  'TGint1-to-SUT1-to-SUT2-to-TGint2',
+                                  'TGint2-to-SUT2-to-SUT1-to-TGint1',
+                                  'TGint2-to-SUT2-to-SUT1-to-TGint1',
+                                  'TGint2-to-SUT2-to-SUT1-to-TGint1',
+                                  'TGint2-to-SUT2-to-SUT1-to-TGint1',
+                                  'TGint2-to-SUT2-to-SUT1-to-TGint1',
+                                  'TGint2-to-SUT2-to-SUT1-to-TGint1'],
+                               y=df[col],
+                               name=name,
+                               **plot["traces"]))
+
+    try:
+        # Create plot
+        logging.info("    Writing file '{0}{1}'.".
+                     format(plot["output-file"], plot["output-file-type"]))
+        plpl = plgo.Figure(data=traces, layout=plot["layout"])
+
+        # Export Plot
+        ploff.plot(plpl,
+                   show_link=False, auto_open=False,
+                   filename='{0}{1}'.format(plot["output-file"],
+                                            plot["output-file-type"]))
+    except PlotlyError as err:
+        logging.error("   Finished with error: {}".
+                      format(str(err).replace("\n", " ")))
+        return
+
+    logging.info("  Done.")
+
+
+def plot_throughput_speedup_analysis(plot, input_data):
+    """Generate the plot(s) with algorithm: plot_throughput_speedup_analysis
+    specified in the specification file.
+
+    :param plot: Plot to generate.
+    :param input_data: Data to process.
+    :type plot: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the plot {0} ...".
+                 format(plot.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(plot.get("type", ""), plot.get("title", "")))
+    data = input_data.filter_data(plot)
+    if data is None:
+        logging.error("No data.")
+        return
+
+    throughput = dict()
+    for job in data:
+        for build in job:
+            for test in build:
+                if throughput.get(test["parent"], None) is None:
+                    throughput[test["parent"]] = {"1": list(),
+                                                  "2": list(),
+                                                  "4": list()}
+                try:
+                    if "1T1C" in test["tags"]:
+                        throughput[test["parent"]]["1"].\
+                            append(test["throughput"]["value"])
+                    elif "2T2C" in test["tags"]:
+                        throughput[test["parent"]]["2"]. \
+                            append(test["throughput"]["value"])
+                    elif "4T4C" in test["tags"]:
+                        throughput[test["parent"]]["4"]. \
+                            append(test["throughput"]["value"])
+                except (KeyError, TypeError):
+                    pass
+
+    if not throughput:
+        logging.warning("No data for the plot '{}'".
+                        format(plot.get("title", "")))
+        return
+
+    for test_name, test_vals in throughput.items():
+        for key, test_val in test_vals.items():
+            if test_val:
+                throughput[test_name][key] = sum(test_val) / len(test_val)
+
+    names = ['1 core', '2 cores', '4 cores']
+    x_vals = list()
+    y_vals_1 = list()
+    y_vals_2 = list()
+    y_vals_4 = list()
+
+    for test_name, test_vals in throughput.items():
+        if test_vals["1"]:
+            x_vals.append("-".join(test_name.split('-')[1:-1]))
+            y_vals_1.append(1)
+            if test_vals["2"]:
+                y_vals_2.append(
+                    round(float(test_vals["2"]) / float(test_vals["1"]), 2))
+            else:
+                y_vals_2.append(None)
+            if test_vals["4"]:
+                y_vals_4.append(
+                    round(float(test_vals["4"]) / float(test_vals["1"]), 2))
+            else:
+                y_vals_4.append(None)
+
+    y_vals = [y_vals_1, y_vals_2, y_vals_4]
+
+    y_vals_zipped = zip(names, y_vals)
+    traces = list()
+    for val in y_vals_zipped:
+        traces.append(plgo.Bar(x=x_vals,
+                               y=val[1],
+                               name=val[0]))
+
+    try:
+        # Create plot
+        logging.info("    Writing file '{0}{1}'.".
+                     format(plot["output-file"], plot["output-file-type"]))
+        plpl = plgo.Figure(data=traces, layout=plot["layout"])
+
+        # Export Plot
+        ploff.plot(plpl,
+                   show_link=False, auto_open=False,
+                   filename='{0}{1}'.format(plot["output-file"],
+                                            plot["output-file-type"]))
+    except PlotlyError as err:
+        logging.error("   Finished with error: {}".
+                      format(str(err).replace("\n", " ")))
+        return
+
+    logging.info("  Done.")
+
+
+def plot_http_server_performance_box(plot, input_data):
+    """Generate the plot(s) with algorithm: plot_http_server_performance_box
+    specified in the specification file.
+
+    :param plot: Plot to generate.
+    :param input_data: Data to process.
+    :type plot: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the plot {0} ...".
+                 format(plot.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(plot.get("type", ""), plot.get("title", "")))
+    data = input_data.filter_data(plot)
+    if data is None:
+        logging.error("No data.")
+        return
+
+    # Prepare the data for the plot
+    y_vals = dict()
+    for job in data:
+        for build in job:
+            for test in build:
+                if y_vals.get(test["name"], None) is None:
+                    y_vals[test["name"]] = list()
+                try:
+                    y_vals[test["name"]].append(test["result"]["value"])
+                except (KeyError, TypeError):
+                    y_vals[test["name"]].append(None)
+
+    # Add None to the lists with missing data
+    max_len = 0
+    for val in y_vals.values():
+        if len(val) > max_len:
+            max_len = len(val)
+    for key, val in y_vals.items():
+        if len(val) < max_len:
+            val.extend([None for _ in range(max_len - len(val))])
+
+    # Add plot traces
+    traces = list()
+    df = pd.DataFrame(y_vals)
+    df.head()
+    for i, col in enumerate(df.columns):
+        name = "{0}. {1}".format(i + 1, col.lower().replace('-cps', '').
+                                 replace('-rps', ''))
+        traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
+                               y=df[col],
+                               name=name,
+                               **plot["traces"]))
+    try:
+        # Create plot
+        plpl = plgo.Figure(data=traces, layout=plot["layout"])
+
+        # Export Plot
+        logging.info("    Writing file '{0}{1}'.".
+                     format(plot["output-file"], plot["output-file-type"]))
+        ploff.plot(plpl,
+                   show_link=False, auto_open=False,
+                   filename='{0}{1}'.format(plot["output-file"],
+                                            plot["output-file-type"]))
+    except PlotlyError as err:
+        logging.error("   Finished with error: {}".
+                      format(str(err).replace("\n", " ")))
+        return
+
+    logging.info("  Done.")
diff --git a/resources/tools/presentation/new/generator_report.py b/resources/tools/presentation/new/generator_report.py
new file mode 100644 (file)
index 0000000..07103db
--- /dev/null
@@ -0,0 +1,191 @@
+# Copyright (c) 2017 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Report generation.
+"""
+
+import logging
+import datetime
+
+from shutil import make_archive
+
+from utils import get_files, execute_command, archive_input_data
+
+
+# .css file for the html format of the report
+THEME_OVERRIDES = """/* override table width restrictions */
+@media screen and (min-width: 767px) {
+    .wy-table-responsive table td, .wy-table-responsive table th {
+        white-space: normal !important;
+    }
+
+    .wy-table-responsive {
+        font-size: small;
+        margin-bottom: 24px;
+        max-width: 100%;
+        overflow: visible !important;
+    }
+}
+.rst-content blockquote {
+    margin-left: 0px;
+    line-height: 18px;
+    margin-bottom: 0px;
+}
+"""
+
+# Command to build the html format of the report
+HTML_BUILDER = 'sphinx-build -v -c . -a ' \
+               '-b html -E ' \
+               '-t html ' \
+               '-D release={release} ' \
+               '-D version="{release} report - {date}" ' \
+               '{working_dir} ' \
+               '{build_dir}/'
+
+# Command to build the pdf format of the report
+PDF_BUILDER = 'sphinx-build -v -c . -a ' \
+              '-b latex -E ' \
+              '-t latex ' \
+              '-D release={release} ' \
+              '-D version="{release} report - {date}" ' \
+              '{working_dir} ' \
+              '{build_dir}'
+
+
+def generate_report(release, spec):
+    """Generate all formats and versions of the report.
+
+    :param release: Release string of the product.
+    :param spec: Specification read from the specification file.
+    :type release: str
+    :type spec: Specification
+    """
+
+    logging.info("Generating the report ...")
+
+    report = {
+        "html": generate_html_report,
+        "pdf": generate_pdf_report
+    }
+
+    for report_format, versions in spec.output["format"].items():
+        report[report_format](release, spec, versions)
+
+    archive_input_data(spec)
+    archive_report(spec)
+
+    logging.info("Done.")
+
+
+def generate_html_report(release, spec, versions):
+    """Generate html format of the report.
+
+    :param release: Release string of the product.
+    :param spec: Specification read from the specification file.
+    :param versions: List of versions to generate.
+    :type release: str
+    :type spec: Specification
+    :type versions: list
+    """
+
+    logging.info("  Generating the html report, give me a few minutes, please "
+                 "...")
+
+    cmd = HTML_BUILDER.format(
+        release=release,
+        date=datetime.datetime.utcnow().strftime('%m/%d/%Y %H:%M UTC'),
+        working_dir=spec.environment["paths"]["DIR[WORKING,SRC]"],
+        build_dir=spec.environment["paths"]["DIR[BUILD,HTML]"])
+    execute_command(cmd)
+
+    with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE]"], "w") as \
+            css_file:
+        css_file.write(THEME_OVERRIDES)
+
+    with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE2]"], "w") as \
+            css_file:
+        css_file.write(THEME_OVERRIDES)
+
+    logging.info("  Done.")
+
+
+def generate_pdf_report(release, spec, versions):
+    """Generate html format of the report.
+
+    :param release: Release string of the product.
+    :param spec: Specification read from the specification file.
+    :param versions: List of versions to generate. Not implemented yet.
+    :type release: str
+    :type spec: Specification
+    :type versions: list
+    """
+
+    logging.info("  Generating the pdf report, give me a few minutes, please "
+                 "...")
+
+    convert_plots = "xvfb-run -a wkhtmltopdf {html} {pdf}.pdf"
+
+    # Convert PyPLOT graphs in HTML format to PDF.
+    plots = get_files(spec.environment["paths"]["DIR[STATIC,VPP]"], "html")
+    plots.extend(get_files(spec.environment["paths"]["DIR[STATIC,DPDK]"],
+                           "html"))
+    for plot in plots:
+        file_name = "{0}".format(plot.rsplit(".", 1)[0])
+        cmd = convert_plots.format(html=plot, pdf=file_name)
+        execute_command(cmd)
+
+    # Generate the LaTeX documentation
+    build_dir = spec.environment["paths"]["DIR[BUILD,LATEX]"]
+    cmd = PDF_BUILDER.format(
+        release=release,
+        date=datetime.datetime.utcnow().strftime('%m/%d/%Y %H:%M UTC'),
+        working_dir=spec.environment["paths"]["DIR[WORKING,SRC]"],
+        build_dir=build_dir)
+    execute_command(cmd)
+
+    # Build pdf documentation
+    archive_dir = spec.environment["paths"]["DIR[STATIC,ARCH]"]
+    cmds = [
+        'cd {build_dir} && '
+        'pdflatex -shell-escape -interaction nonstopmode csit.tex || true'.
+        format(build_dir=build_dir),
+        'cd {build_dir} && '
+        'pdflatex -interaction nonstopmode csit.tex || true'.
+        format(build_dir=build_dir),
+        'cd {build_dir} && '
+        'cp csit.pdf ../{archive_dir}/csit_{release}.pdf'.
+        format(build_dir=build_dir,
+               archive_dir=archive_dir,
+               release=release)
+    ]
+
+    for cmd in cmds:
+        execute_command(cmd)
+
+    logging.info("  Done.")
+
+
+def archive_report(spec):
+    """Archive the report.
+
+    :param spec: Specification read from the specification file.
+    :type spec: Specification
+    """
+
+    logging.info("  Archiving the report ...")
+
+    make_archive("csit.report",
+                 "gztar",
+                 base_dir=spec.environment["paths"]["DIR[BUILD,HTML]"])
+
+    logging.info("  Done.")
diff --git a/resources/tools/presentation/new/generator_tables.py b/resources/tools/presentation/new/generator_tables.py
new file mode 100644 (file)
index 0000000..12f1601
--- /dev/null
@@ -0,0 +1,995 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Algorithms to generate tables.
+"""
+
+
+import logging
+import csv
+import prettytable
+import pandas as pd
+
+from string import replace
+from collections import OrderedDict
+from numpy import nan, isnan
+from xml.etree import ElementTree as ET
+
+from errors import PresentationError
+from utils import mean, stdev, relative_change, classify_anomalies
+
+
+def generate_tables(spec, data):
+    """Generate all tables specified in the specification file.
+
+    :param spec: Specification read from the specification file.
+    :param data: Data to process.
+    :type spec: Specification
+    :type data: InputData
+    """
+
+    logging.info("Generating the tables ...")
+    for table in spec.tables:
+        try:
+            eval(table["algorithm"])(table, data)
+        except NameError as err:
+            logging.error("Probably algorithm '{alg}' is not defined: {err}".
+                          format(alg=table["algorithm"], err=repr(err)))
+    logging.info("Done.")
+
+
+def table_details(table, input_data):
+    """Generate the table(s) with algorithm: table_detailed_test_results
+    specified in the specification file.
+
+    :param table: Table to generate.
+    :param input_data: Data to process.
+    :type table: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the table {0} ...".
+                 format(table.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(table.get("type", ""), table.get("title", "")))
+    data = input_data.filter_data(table)
+
+    # Prepare the header of the tables
+    header = list()
+    for column in table["columns"]:
+        header.append('"{0}"'.format(str(column["title"]).replace('"', '""')))
+
+    # Generate the data for the table according to the model in the table
+    # specification
+    job = table["data"].keys()[0]
+    build = str(table["data"][job][0])
+    try:
+        suites = input_data.suites(job, build)
+    except KeyError:
+        logging.error("    No data available. The table will not be generated.")
+        return
+
+    for suite_longname, suite in suites.iteritems():
+        # Generate data
+        suite_name = suite["name"]
+        table_lst = list()
+        for test in data[job][build].keys():
+            if data[job][build][test]["parent"] in suite_name:
+                row_lst = list()
+                for column in table["columns"]:
+                    try:
+                        col_data = str(data[job][build][test][column["data"].
+                                       split(" ")[1]]).replace('"', '""')
+                        if column["data"].split(" ")[1] in ("vat-history",
+                                                            "show-run"):
+                            col_data = replace(col_data, " |br| ", "",
+                                               maxreplace=1)
+                            col_data = " |prein| {0} |preout| ".\
+                                format(col_data[:-5])
+                        row_lst.append('"{0}"'.format(col_data))
+                    except KeyError:
+                        row_lst.append("No data")
+                table_lst.append(row_lst)
+
+        # Write the data to file
+        if table_lst:
+            file_name = "{0}_{1}{2}".format(table["output-file"], suite_name,
+                                            table["output-file-ext"])
+            logging.info("      Writing file: '{}'".format(file_name))
+            with open(file_name, "w") as file_handler:
+                file_handler.write(",".join(header) + "\n")
+                for item in table_lst:
+                    file_handler.write(",".join(item) + "\n")
+
+    logging.info("  Done.")
+
+
+def table_merged_details(table, input_data):
+    """Generate the table(s) with algorithm: table_merged_details
+    specified in the specification file.
+
+    :param table: Table to generate.
+    :param input_data: Data to process.
+    :type table: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the table {0} ...".
+                 format(table.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(table.get("type", ""), table.get("title", "")))
+    data = input_data.filter_data(table)
+    data = input_data.merge_data(data)
+    data.sort_index(inplace=True)
+
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(table.get("type", ""), table.get("title", "")))
+    suites = input_data.filter_data(table, data_set="suites")
+    suites = input_data.merge_data(suites)
+
+    # Prepare the header of the tables
+    header = list()
+    for column in table["columns"]:
+        header.append('"{0}"'.format(str(column["title"]).replace('"', '""')))
+
+    for _, suite in suites.iteritems():
+        # Generate data
+        suite_name = suite["name"]
+        table_lst = list()
+        for test in data.keys():
+            if data[test]["parent"] in suite_name:
+                row_lst = list()
+                for column in table["columns"]:
+                    try:
+                        col_data = str(data[test][column["data"].
+                                       split(" ")[1]]).replace('"', '""')
+                        if column["data"].split(" ")[1] in ("vat-history",
+                                                            "show-run"):
+                            col_data = replace(col_data, " |br| ", "",
+                                               maxreplace=1)
+                            col_data = " |prein| {0} |preout| ".\
+                                format(col_data[:-5])
+                        row_lst.append('"{0}"'.format(col_data))
+                    except KeyError:
+                        row_lst.append("No data")
+                table_lst.append(row_lst)
+
+        # Write the data to file
+        if table_lst:
+            file_name = "{0}_{1}{2}".format(table["output-file"], suite_name,
+                                            table["output-file-ext"])
+            logging.info("      Writing file: '{}'".format(file_name))
+            with open(file_name, "w") as file_handler:
+                file_handler.write(",".join(header) + "\n")
+                for item in table_lst:
+                    file_handler.write(",".join(item) + "\n")
+
+    logging.info("  Done.")
+
+
+def table_performance_improvements(table, input_data):
+    """Generate the table(s) with algorithm: table_performance_improvements
+    specified in the specification file.
+
+    :param table: Table to generate.
+    :param input_data: Data to process.
+    :type table: pandas.Series
+    :type input_data: InputData
+    """
+
+    def _write_line_to_file(file_handler, data):
+        """Write a line to the .csv file.
+
+        :param file_handler: File handler for the csv file. It must be open for
+         writing text.
+        :param data: Item to be written to the file.
+        :type file_handler: BinaryIO
+        :type data: list
+        """
+
+        line_lst = list()
+        for item in data:
+            if isinstance(item["data"], str):
+                # Remove -?drdisc from the end
+                if item["data"].endswith("drdisc"):
+                    item["data"] = item["data"][:-8]
+                line_lst.append(item["data"])
+            elif isinstance(item["data"], float):
+                line_lst.append("{:.1f}".format(item["data"]))
+            elif item["data"] is None:
+                line_lst.append("")
+        file_handler.write(",".join(line_lst) + "\n")
+
+    logging.info("  Generating the table {0} ...".
+                 format(table.get("title", "")))
+
+    # Read the template
+    file_name = table.get("template", None)
+    if file_name:
+        try:
+            tmpl = _read_csv_template(file_name)
+        except PresentationError:
+            logging.error("  The template '{0}' does not exist. Skipping the "
+                          "table.".format(file_name))
+            return None
+    else:
+        logging.error("The template is not defined. Skipping the table.")
+        return None
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(table.get("type", ""), table.get("title", "")))
+    data = input_data.filter_data(table)
+
+    # Prepare the header of the tables
+    header = list()
+    for column in table["columns"]:
+        header.append(column["title"])
+
+    # Generate the data for the table according to the model in the table
+    # specification
+    tbl_lst = list()
+    for tmpl_item in tmpl:
+        tbl_item = list()
+        for column in table["columns"]:
+            cmd = column["data"].split(" ")[0]
+            args = column["data"].split(" ")[1:]
+            if cmd == "template":
+                try:
+                    val = float(tmpl_item[int(args[0])])
+                except ValueError:
+                    val = tmpl_item[int(args[0])]
+                tbl_item.append({"data": val})
+            elif cmd == "data":
+                jobs = args[0:-1]
+                operation = args[-1]
+                data_lst = list()
+                for job in jobs:
+                    for build in data[job]:
+                        try:
+                            data_lst.append(float(build[tmpl_item[0]]
+                                                  ["throughput"]["value"]))
+                        except (KeyError, TypeError):
+                            # No data, ignore
+                            continue
+                if data_lst:
+                    tbl_item.append({"data": (eval(operation)(data_lst)) /
+                                             1000000})
+                else:
+                    tbl_item.append({"data": None})
+            elif cmd == "operation":
+                operation = args[0]
+                try:
+                    nr1 = float(tbl_item[int(args[1])]["data"])
+                    nr2 = float(tbl_item[int(args[2])]["data"])
+                    if nr1 and nr2:
+                        tbl_item.append({"data": eval(operation)(nr1, nr2)})
+                    else:
+                        tbl_item.append({"data": None})
+                except (IndexError, ValueError, TypeError):
+                    logging.error("No data for {0}".format(tbl_item[0]["data"]))
+                    tbl_item.append({"data": None})
+                    continue
+            else:
+                logging.error("Not supported command {0}. Skipping the table.".
+                              format(cmd))
+                return None
+        tbl_lst.append(tbl_item)
+
+    # Sort the table according to the relative change
+    tbl_lst.sort(key=lambda rel: rel[-1]["data"], reverse=True)
+
+    # Create the tables and write them to the files
+    file_names = [
+        "{0}_ndr_top{1}".format(table["output-file"], table["output-file-ext"]),
+        "{0}_pdr_top{1}".format(table["output-file"], table["output-file-ext"]),
+        "{0}_ndr_low{1}".format(table["output-file"], table["output-file-ext"]),
+        "{0}_pdr_low{1}".format(table["output-file"], table["output-file-ext"])
+    ]
+
+    for file_name in file_names:
+        logging.info("    Writing the file '{0}'".format(file_name))
+        with open(file_name, "w") as file_handler:
+            file_handler.write(",".join(header) + "\n")
+            for item in tbl_lst:
+                if isinstance(item[-1]["data"], float):
+                    rel_change = round(item[-1]["data"], 1)
+                else:
+                    rel_change = item[-1]["data"]
+                if "ndr_top" in file_name \
+                        and "ndr" in item[0]["data"] \
+                        and rel_change >= 10.0:
+                    _write_line_to_file(file_handler, item)
+                elif "pdr_top" in file_name \
+                        and "pdr" in item[0]["data"] \
+                        and rel_change >= 10.0:
+                    _write_line_to_file(file_handler, item)
+                elif "ndr_low" in file_name \
+                        and "ndr" in item[0]["data"] \
+                        and rel_change < 10.0:
+                    _write_line_to_file(file_handler, item)
+                elif "pdr_low" in file_name \
+                        and "pdr" in item[0]["data"] \
+                        and rel_change < 10.0:
+                    _write_line_to_file(file_handler, item)
+
+    logging.info("  Done.")
+
+
+def _read_csv_template(file_name):
+    """Read the template from a .csv file.
+
+    :param file_name: Name / full path / relative path of the file to read.
+    :type file_name: str
+    :returns: Data from the template as list (lines) of lists (items on line).
+    :rtype: list
+    :raises: PresentationError if it is not possible to read the file.
+    """
+
+    try:
+        with open(file_name, 'r') as csv_file:
+            tmpl_data = list()
+            for line in csv_file:
+                tmpl_data.append(line[:-1].split(","))
+        return tmpl_data
+    except IOError as err:
+        raise PresentationError(str(err), level="ERROR")
+
+
+def table_performance_comparison(table, input_data):
+    """Generate the table(s) with algorithm: table_performance_comparison
+    specified in the specification file.
+
+    :param table: Table to generate.
+    :param input_data: Data to process.
+    :type table: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the table {0} ...".
+                 format(table.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(table.get("type", ""), table.get("title", "")))
+    data = input_data.filter_data(table, continue_on_error=True)
+
+    # Prepare the header of the tables
+    try:
+        header = ["Test case", ]
+
+        history = table.get("history", None)
+        if history:
+            for item in history:
+                header.extend(
+                    ["{0} Throughput [Mpps]".format(item["title"]),
+                     "{0} Stdev [Mpps]".format(item["title"])])
+        header.extend(
+            ["{0} Throughput [Mpps]".format(table["reference"]["title"]),
+             "{0} Stdev [Mpps]".format(table["reference"]["title"]),
+             "{0} Throughput [Mpps]".format(table["compare"]["title"]),
+             "{0} Stdev [Mpps]".format(table["compare"]["title"]),
+             "Change [%]"])
+        header_str = ",".join(header) + "\n"
+    except (AttributeError, KeyError) as err:
+        logging.error("The model is invalid, missing parameter: {0}".
+                      format(err))
+        return
+
+    # Prepare data to the table:
+    tbl_dict = dict()
+    for job, builds in table["reference"]["data"].items():
+        for build in builds:
+            for tst_name, tst_data in data[job][str(build)].iteritems():
+                if tbl_dict.get(tst_name, None) is None:
+                    name = "{0}-{1}".format(tst_data["parent"].split("-")[0],
+                                            "-".join(tst_data["name"].
+                                                     split("-")[1:]))
+                    tbl_dict[tst_name] = {"name": name,
+                                          "ref-data": list(),
+                                          "cmp-data": list()}
+                try:
+                    tbl_dict[tst_name]["ref-data"].\
+                        append(tst_data["throughput"]["value"])
+                except TypeError:
+                    pass  # No data in output.xml for this test
+
+    for job, builds in table["compare"]["data"].items():
+        for build in builds:
+            for tst_name, tst_data in data[job][str(build)].iteritems():
+                try:
+                    tbl_dict[tst_name]["cmp-data"].\
+                        append(tst_data["throughput"]["value"])
+                except KeyError:
+                    pass
+                except TypeError:
+                    tbl_dict.pop(tst_name, None)
+    if history:
+        for item in history:
+            for job, builds in item["data"].items():
+                for build in builds:
+                    for tst_name, tst_data in data[job][str(build)].iteritems():
+                        if tbl_dict.get(tst_name, None) is None:
+                            continue
+                        if tbl_dict[tst_name].get("history", None) is None:
+                            tbl_dict[tst_name]["history"] = OrderedDict()
+                        if tbl_dict[tst_name]["history"].get(item["title"],
+                                                             None) is None:
+                            tbl_dict[tst_name]["history"][item["title"]] = \
+                                list()
+                        try:
+                            tbl_dict[tst_name]["history"][item["title"]].\
+                                append(tst_data["throughput"]["value"])
+                        except (TypeError, KeyError):
+                            pass
+
+    tbl_lst = list()
+    for tst_name in tbl_dict.keys():
+        item = [tbl_dict[tst_name]["name"], ]
+        if history:
+            if tbl_dict[tst_name].get("history", None) is not None:
+                for hist_data in tbl_dict[tst_name]["history"].values():
+                    if hist_data:
+                        item.append(round(mean(hist_data) / 1000000, 2))
+                        item.append(round(stdev(hist_data) / 1000000, 2))
+                    else:
+                        item.extend([None, None])
+            else:
+                item.extend([None, None])
+        if tbl_dict[tst_name]["ref-data"]:
+            data_t = tbl_dict[tst_name]["ref-data"]
+            # TODO: Specify window size.
+            if data_t:
+                item.append(round(mean(data_t) / 1000000, 2))
+                item.append(round(stdev(data_t) / 1000000, 2))
+            else:
+                item.extend([None, None])
+        else:
+            item.extend([None, None])
+        if tbl_dict[tst_name]["cmp-data"]:
+            data_t = tbl_dict[tst_name]["cmp-data"]
+            # TODO: Specify window size.
+            if data_t:
+                item.append(round(mean(data_t) / 1000000, 2))
+                item.append(round(stdev(data_t) / 1000000, 2))
+            else:
+                item.extend([None, None])
+        else:
+            item.extend([None, None])
+        if item[-4] is not None and item[-2] is not None and item[-4] != 0:
+            item.append(int(relative_change(float(item[-4]), float(item[-2]))))
+        if len(item) == len(header):
+            tbl_lst.append(item)
+
+    # Sort the table according to the relative change
+    tbl_lst.sort(key=lambda rel: rel[-1], reverse=True)
+
+    # Generate tables:
+    # All tests in csv:
+    tbl_names = ["{0}-ndr-1t1c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"]),
+                 "{0}-ndr-2t2c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"]),
+                 "{0}-ndr-4t4c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"]),
+                 "{0}-pdr-1t1c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"]),
+                 "{0}-pdr-2t2c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"]),
+                 "{0}-pdr-4t4c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"])
+                 ]
+    for file_name in tbl_names:
+        logging.info("      Writing file: '{0}'".format(file_name))
+        with open(file_name, "w") as file_handler:
+            file_handler.write(header_str)
+            for test in tbl_lst:
+                if (file_name.split("-")[-3] in test[0] and    # NDR vs PDR
+                        file_name.split("-")[-2] in test[0]):  # cores
+                    test[0] = "-".join(test[0].split("-")[:-1])
+                    file_handler.write(",".join([str(item) for item in test]) +
+                                       "\n")
+
+    # All tests in txt:
+    tbl_names_txt = ["{0}-ndr-1t1c-full.txt".format(table["output-file"]),
+                     "{0}-ndr-2t2c-full.txt".format(table["output-file"]),
+                     "{0}-ndr-4t4c-full.txt".format(table["output-file"]),
+                     "{0}-pdr-1t1c-full.txt".format(table["output-file"]),
+                     "{0}-pdr-2t2c-full.txt".format(table["output-file"]),
+                     "{0}-pdr-4t4c-full.txt".format(table["output-file"])
+                     ]
+
+    for i, txt_name in enumerate(tbl_names_txt):
+        txt_table = None
+        logging.info("      Writing file: '{0}'".format(txt_name))
+        with open(tbl_names[i], 'rb') as csv_file:
+            csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
+            for row in csv_content:
+                if txt_table is None:
+                    txt_table = prettytable.PrettyTable(row)
+                else:
+                    txt_table.add_row(row)
+            txt_table.align["Test case"] = "l"
+        with open(txt_name, "w") as txt_file:
+            txt_file.write(str(txt_table))
+
+    # Selected tests in csv:
+    input_file = "{0}-ndr-1t1c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"])
+    with open(input_file, "r") as in_file:
+        lines = list()
+        for line in in_file:
+            lines.append(line)
+
+    output_file = "{0}-ndr-1t1c-top{1}".format(table["output-file"],
+                                               table["output-file-ext"])
+    logging.info("      Writing file: '{0}'".format(output_file))
+    with open(output_file, "w") as out_file:
+        out_file.write(header_str)
+        for i, line in enumerate(lines[1:]):
+            if i == table["nr-of-tests-shown"]:
+                break
+            out_file.write(line)
+
+    output_file = "{0}-ndr-1t1c-bottom{1}".format(table["output-file"],
+                                                  table["output-file-ext"])
+    logging.info("      Writing file: '{0}'".format(output_file))
+    with open(output_file, "w") as out_file:
+        out_file.write(header_str)
+        for i, line in enumerate(lines[-1:0:-1]):
+            if i == table["nr-of-tests-shown"]:
+                break
+            out_file.write(line)
+
+    input_file = "{0}-pdr-1t1c-full{1}".format(table["output-file"],
+                                               table["output-file-ext"])
+    with open(input_file, "r") as in_file:
+        lines = list()
+        for line in in_file:
+            lines.append(line)
+
+    output_file = "{0}-pdr-1t1c-top{1}".format(table["output-file"],
+                                               table["output-file-ext"])
+    logging.info("      Writing file: '{0}'".format(output_file))
+    with open(output_file, "w") as out_file:
+        out_file.write(header_str)
+        for i, line in enumerate(lines[1:]):
+            if i == table["nr-of-tests-shown"]:
+                break
+            out_file.write(line)
+
+    output_file = "{0}-pdr-1t1c-bottom{1}".format(table["output-file"],
+                                                  table["output-file-ext"])
+    logging.info("      Writing file: '{0}'".format(output_file))
+    with open(output_file, "w") as out_file:
+        out_file.write(header_str)
+        for i, line in enumerate(lines[-1:0:-1]):
+            if i == table["nr-of-tests-shown"]:
+                break
+            out_file.write(line)
+
+
+def table_performance_comparison_mrr(table, input_data):
+    """Generate the table(s) with algorithm: table_performance_comparison_mrr
+    specified in the specification file.
+
+    :param table: Table to generate.
+    :param input_data: Data to process.
+    :type table: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the table {0} ...".
+                 format(table.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(table.get("type", ""), table.get("title", "")))
+    data = input_data.filter_data(table, continue_on_error=True)
+
+    # Prepare the header of the tables
+    try:
+        header = ["Test case",
+                  "{0} Throughput [Mpps]".format(table["reference"]["title"]),
+                  "{0} stdev [Mpps]".format(table["reference"]["title"]),
+                  "{0} Throughput [Mpps]".format(table["compare"]["title"]),
+                  "{0} stdev [Mpps]".format(table["compare"]["title"]),
+                  "Change [%]"]
+        header_str = ",".join(header) + "\n"
+    except (AttributeError, KeyError) as err:
+        logging.error("The model is invalid, missing parameter: {0}".
+                      format(err))
+        return
+
+    # Prepare data to the table:
+    tbl_dict = dict()
+    for job, builds in table["reference"]["data"].items():
+        for build in builds:
+            for tst_name, tst_data in data[job][str(build)].iteritems():
+                if tbl_dict.get(tst_name, None) is None:
+                    name = "{0}-{1}".format(tst_data["parent"].split("-")[0],
+                                            "-".join(tst_data["name"].
+                                                     split("-")[1:]))
+                    tbl_dict[tst_name] = {"name": name,
+                                          "ref-data": list(),
+                                          "cmp-data": list()}
+                try:
+                    tbl_dict[tst_name]["ref-data"].\
+                        append(tst_data["result"]["throughput"])
+                except TypeError:
+                    pass  # No data in output.xml for this test
+
+    for job, builds in table["compare"]["data"].items():
+        for build in builds:
+            for tst_name, tst_data in data[job][str(build)].iteritems():
+                try:
+                    tbl_dict[tst_name]["cmp-data"].\
+                        append(tst_data["result"]["throughput"])
+                except KeyError:
+                    pass
+                except TypeError:
+                    tbl_dict.pop(tst_name, None)
+
+    tbl_lst = list()
+    for tst_name in tbl_dict.keys():
+        item = [tbl_dict[tst_name]["name"], ]
+        if tbl_dict[tst_name]["ref-data"]:
+            data_t = tbl_dict[tst_name]["ref-data"]
+            # TODO: Specify window size.
+            if data_t:
+                item.append(round(mean(data_t) / 1000000, 2))
+                item.append(round(stdev(data_t) / 1000000, 2))
+            else:
+                item.extend([None, None])
+        else:
+            item.extend([None, None])
+        if tbl_dict[tst_name]["cmp-data"]:
+            data_t = tbl_dict[tst_name]["cmp-data"]
+            # TODO: Specify window size.
+            if data_t:
+                item.append(round(mean(data_t) / 1000000, 2))
+                item.append(round(stdev(data_t) / 1000000, 2))
+            else:
+                item.extend([None, None])
+        else:
+            item.extend([None, None])
+        if item[1] is not None and item[3] is not None and item[1] != 0:
+            item.append(int(relative_change(float(item[1]), float(item[3]))))
+        if len(item) == 6:
+            tbl_lst.append(item)
+
+    # Sort the table according to the relative change
+    tbl_lst.sort(key=lambda rel: rel[-1], reverse=True)
+
+    # Generate tables:
+    # All tests in csv:
+    tbl_names = ["{0}-1t1c-full{1}".format(table["output-file"],
+                                           table["output-file-ext"]),
+                 "{0}-2t2c-full{1}".format(table["output-file"],
+                                           table["output-file-ext"]),
+                 "{0}-4t4c-full{1}".format(table["output-file"],
+                                           table["output-file-ext"])
+                 ]
+    for file_name in tbl_names:
+        logging.info("      Writing file: '{0}'".format(file_name))
+        with open(file_name, "w") as file_handler:
+            file_handler.write(header_str)
+            for test in tbl_lst:
+                if file_name.split("-")[-2] in test[0]:  # cores
+                    test[0] = "-".join(test[0].split("-")[:-1])
+                    file_handler.write(",".join([str(item) for item in test]) +
+                                       "\n")
+
+    # All tests in txt:
+    tbl_names_txt = ["{0}-1t1c-full.txt".format(table["output-file"]),
+                     "{0}-2t2c-full.txt".format(table["output-file"]),
+                     "{0}-4t4c-full.txt".format(table["output-file"])
+                     ]
+
+    for i, txt_name in enumerate(tbl_names_txt):
+        txt_table = None
+        logging.info("      Writing file: '{0}'".format(txt_name))
+        with open(tbl_names[i], 'rb') as csv_file:
+            csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
+            for row in csv_content:
+                if txt_table is None:
+                    txt_table = prettytable.PrettyTable(row)
+                else:
+                    txt_table.add_row(row)
+            txt_table.align["Test case"] = "l"
+        with open(txt_name, "w") as txt_file:
+            txt_file.write(str(txt_table))
+
+
+def table_performance_trending_dashboard(table, input_data):
+    """Generate the table(s) with algorithm: table_performance_comparison
+    specified in the specification file.
+
+    :param table: Table to generate.
+    :param input_data: Data to process.
+    :type table: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the table {0} ...".
+                 format(table.get("title", "")))
+
+    # Transform the data
+    logging.info("    Creating the data set for the {0} '{1}'.".
+                 format(table.get("type", ""), table.get("title", "")))
+    data = input_data.filter_data(table, continue_on_error=True)
+
+    # Prepare the header of the tables
+    header = ["Test Case",
+              "Trend [Mpps]",
+              "Short-Term Change [%]",
+              "Long-Term Change [%]",
+              "Regressions [#]",
+              "Progressions [#]",
+              "Outliers [#]"
+              ]
+    header_str = ",".join(header) + "\n"
+
+    # Prepare data to the table:
+    tbl_dict = dict()
+    for job, builds in table["data"].items():
+        for build in builds:
+            for tst_name, tst_data in data[job][str(build)].iteritems():
+                if tst_name.lower() in table["ignore-list"]:
+                    continue
+                if tbl_dict.get(tst_name, None) is None:
+                    name = "{0}-{1}".format(tst_data["parent"].split("-")[0],
+                                            "-".join(tst_data["name"].
+                                                     split("-")[1:]))
+                    tbl_dict[tst_name] = {"name": name,
+                                          "data": OrderedDict()}
+                try:
+                    tbl_dict[tst_name]["data"][str(build)] =  \
+                        tst_data["result"]["throughput"]
+                except (TypeError, KeyError):
+                    pass  # No data in output.xml for this test
+
+    tbl_lst = list()
+    for tst_name in tbl_dict.keys():
+        if len(tbl_dict[tst_name]["data"]) < 3:
+            continue
+
+        pd_data = pd.Series(tbl_dict[tst_name]["data"])
+        last_key = pd_data.keys()[-1]
+        win_size = min(pd_data.size, table["window"])
+        win_first_idx = pd_data.size - win_size
+        key_14 = pd_data.keys()[win_first_idx]
+        long_win_size = min(pd_data.size, table["long-trend-window"])
+        median_t = pd_data.rolling(window=win_size, min_periods=2).median()
+        median_first_idx = median_t.size - long_win_size
+        try:
+            max_median = max(
+                [x for x in median_t.values[median_first_idx:-win_size]
+                 if not isnan(x)])
+        except ValueError:
+            max_median = nan
+        try:
+            last_median_t = median_t[last_key]
+        except KeyError:
+            last_median_t = nan
+        try:
+            median_t_14 = median_t[key_14]
+        except KeyError:
+            median_t_14 = nan
+
+        if isnan(last_median_t) or isnan(median_t_14) or median_t_14 == 0.0:
+            rel_change_last = nan
+        else:
+            rel_change_last = round(
+                ((last_median_t - median_t_14) / median_t_14) * 100, 2)
+
+        if isnan(max_median) or isnan(last_median_t) or max_median == 0.0:
+            rel_change_long = nan
+        else:
+            rel_change_long = round(
+                ((last_median_t - max_median) / max_median) * 100, 2)
+
+        # Classification list:
+        classification_lst, _ = classify_anomalies(pd_data)
+
+        if classification_lst:
+            if isnan(rel_change_last) and isnan(rel_change_long):
+                continue
+            tbl_lst.append(
+                [tbl_dict[tst_name]["name"],
+                 '-' if isnan(last_median_t) else
+                 round(last_median_t / 1000000, 2),
+                 '-' if isnan(rel_change_last) else rel_change_last,
+                 '-' if isnan(rel_change_long) else rel_change_long,
+                 classification_lst[win_first_idx:].count("regression"),
+                 classification_lst[win_first_idx:].count("progression"),
+                 classification_lst[win_first_idx:].count("outlier")])
+
+    tbl_lst.sort(key=lambda rel: rel[0])
+
+    tbl_sorted = list()
+    for nrr in range(table["window"], -1, -1):
+        tbl_reg = [item for item in tbl_lst if item[4] == nrr]
+        for nrp in range(table["window"], -1, -1):
+            tbl_pro = [item for item in tbl_reg if item[5] == nrp]
+            for nro in range(table["window"], -1, -1):
+                tbl_out = [item for item in tbl_pro if item[6] == nro]
+                tbl_out.sort(key=lambda rel: rel[2])
+                tbl_sorted.extend(tbl_out)
+
+    file_name = "{0}{1}".format(table["output-file"], table["output-file-ext"])
+
+    logging.info("      Writing file: '{0}'".format(file_name))
+    with open(file_name, "w") as file_handler:
+        file_handler.write(header_str)
+        for test in tbl_sorted:
+            file_handler.write(",".join([str(item) for item in test]) + '\n')
+
+    txt_file_name = "{0}.txt".format(table["output-file"])
+    txt_table = None
+    logging.info("      Writing file: '{0}'".format(txt_file_name))
+    with open(file_name, 'rb') as csv_file:
+        csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
+        for row in csv_content:
+            if txt_table is None:
+                txt_table = prettytable.PrettyTable(row)
+            else:
+                txt_table.add_row(row)
+        txt_table.align["Test case"] = "l"
+    with open(txt_file_name, "w") as txt_file:
+        txt_file.write(str(txt_table))
+
+
+def table_performance_trending_dashboard_html(table, input_data):
+    """Generate the table(s) with algorithm:
+    table_performance_trending_dashboard_html specified in the specification
+    file.
+
+    :param table: Table to generate.
+    :param input_data: Data to process.
+    :type table: pandas.Series
+    :type input_data: InputData
+    """
+
+    logging.info("  Generating the table {0} ...".
+                 format(table.get("title", "")))
+
+    try:
+        with open(table["input-file"], 'rb') as csv_file:
+            csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
+            csv_lst = [item for item in csv_content]
+    except KeyError:
+        logging.warning("The input file is not defined.")
+        return
+    except csv.Error as err:
+        logging.warning("Not possible to process the file '{0}'.\n{1}".
+                        format(table["input-file"], err))
+        return
+
+    # Table:
+    dashboard = ET.Element("table", attrib=dict(width="100%", border='0'))
+
+    # Table header:
+    tr = ET.SubElement(dashboard, "tr", attrib=dict(bgcolor="#7eade7"))
+    for idx, item in enumerate(csv_lst[0]):
+        alignment = "left" if idx == 0 else "center"
+        th = ET.SubElement(tr, "th", attrib=dict(align=alignment))
+        th.text = item
+
+    # Rows:
+    colors = {"regression": ("#ffcccc", "#ff9999"),
+              "progression": ("#c6ecc6", "#9fdf9f"),
+              "outlier": ("#e6e6e6", "#cccccc"),
+              "normal": ("#e9f1fb", "#d4e4f7")}
+    for r_idx, row in enumerate(csv_lst[1:]):
+        if int(row[4]):
+            color = "regression"
+        elif int(row[5]):
+            color = "progression"
+        elif int(row[6]):
+            color = "outlier"
+        else:
+            color = "normal"
+        background = colors[color][r_idx % 2]
+        tr = ET.SubElement(dashboard, "tr", attrib=dict(bgcolor=background))
+
+        # Columns:
+        for c_idx, item in enumerate(row):
+            alignment = "left" if c_idx == 0 else "center"
+            td = ET.SubElement(tr, "td", attrib=dict(align=alignment))
+            # Name:
+            url = "../trending/"
+            file_name = ""
+            anchor = "#"
+            feature = ""
+            if c_idx == 0:
+                if "memif" in item:
+                    file_name = "container_memif.html"
+
+                elif "srv6" in item:
+                    file_name = "srv6.html"
+
+                elif "vhost" in item:
+                    if "l2xcbase" in item or "l2bdbasemaclrn" in item:
+                        file_name = "vm_vhost_l2.html"
+                    elif "ip4base" in item:
+                        file_name = "vm_vhost_ip4.html"
+
+                elif "ipsec" in item:
+                    file_name = "ipsec.html"
+
+                elif "ethip4lispip" in item or "ethip4vxlan" in item:
+                    file_name = "ip4_tunnels.html"
+
+                elif "ip4base" in item or "ip4scale" in item:
+                    file_name = "ip4.html"
+                    if "iacl" in item or "snat" in item or "cop" in item:
+                        feature = "-features"
+
+                elif "ip6base" in item or "ip6scale" in item:
+                    file_name = "ip6.html"
+
+                elif "l2xcbase" in item or "l2xcscale" in item \
+                        or "l2bdbasemaclrn" in item or "l2bdscale" in item \
+                        or "l2dbbasemaclrn" in item or "l2dbscale" in item:
+                    file_name = "l2.html"
+                    if "iacl" in item:
+                        feature = "-features"
+
+                if "x520" in item:
+                    anchor += "x520-"
+                elif "x710" in item:
+                    anchor += "x710-"
+                elif "xl710" in item:
+                    anchor += "xl710-"
+
+                if "64b" in item:
+                    anchor += "64b-"
+                elif "78b" in item:
+                    anchor += "78b-"
+                elif "imix" in item:
+                    anchor += "imix-"
+                elif "9000b" in item:
+                    anchor += "9000b-"
+                elif "1518" in item:
+                    anchor += "1518b-"
+
+                if "1t1c" in item:
+                    anchor += "1t1c"
+                elif "2t2c" in item:
+                    anchor += "2t2c"
+                elif "4t4c" in item:
+                    anchor += "4t4c"
+
+                url = url + file_name + anchor + feature
+
+                ref = ET.SubElement(td, "a", attrib=dict(href=url))
+                ref.text = item
+
+            if c_idx > 0:
+                td.text = item
+
+    try:
+        with open(table["output-file"], 'w') as html_file:
+            logging.info("      Writing file: '{0}'".
+                         format(table["output-file"]))
+            html_file.write(".. raw:: html\n\n\t")
+            html_file.write(ET.tostring(dashboard))
+            html_file.write("\n\t<p><br><br></p>\n")
+    except KeyError:
+        logging.warning("The output file is not defined.")
+        return
diff --git a/resources/tools/presentation/new/input_data_files.py b/resources/tools/presentation/new/input_data_files.py
new file mode 100644 (file)
index 0000000..cde6d1a
--- /dev/null
@@ -0,0 +1,230 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Inputs
+Download all data.
+"""
+
+import re
+
+from os import rename, mkdir
+from os.path import join
+from zipfile import ZipFile, is_zipfile, BadZipfile
+from httplib import responses
+from requests import get, codes, RequestException, Timeout, TooManyRedirects, \
+    HTTPError, ConnectionError
+
+from errors import PresentationError
+from utils import execute_command
+
+# Chunk size used for file download
+CHUNK_SIZE = 512
+
+# Separator used in file names
+SEPARATOR = "__"
+
+REGEX_RELEASE = re.compile(r'(\D*)(\d{4}|master)(\D*)')
+
+
+def _download_file(url, file_name, log):
+    """Download a file with input data.
+
+    :param url: URL to the file to download.
+    :param file_name: Name of file to download.
+    :param log: List of log messages.
+    :type url: str
+    :type file_name: str
+    :type log: list of tuples (severity, msg)
+    :returns: True if the download was successful, otherwise False.
+    :rtype: bool
+    """
+
+    success = False
+    try:
+        log.append(("INFO", "    Connecting to '{0}' ...".format(url)))
+
+        response = get(url, stream=True)
+        code = response.status_code
+
+        log.append(("INFO", "    {0}: {1}".format(code, responses[code])))
+
+        if code != codes["OK"]:
+            return False
+
+        log.append(("INFO", "    Downloading the file '{0}' to '{1}' ...".
+                    format(url, file_name)))
+
+        file_handle = open(file_name, "wb")
+        for chunk in response.iter_content(chunk_size=CHUNK_SIZE):
+            if chunk:
+                file_handle.write(chunk)
+        file_handle.close()
+        success = True
+    except ConnectionError as err:
+        log.append(("ERROR", "Not possible to connect to '{0}'.".format(url)))
+        log.append(("DEBUG", str(err)))
+    except HTTPError as err:
+        log.append(("ERROR", "Invalid HTTP response from '{0}'.".format(url)))
+        log.append(("DEBUG", str(err)))
+    except TooManyRedirects as err:
+        log.append(("ERROR", "Request exceeded the configured number "
+                             "of maximum re-directions."))
+        log.append(("DEBUG", str(err)))
+    except Timeout as err:
+        log.append(("ERROR", "Request timed out."))
+        log.append(("DEBUG", str(err)))
+    except RequestException as err:
+        log.append(("ERROR", "Unexpected HTTP request exception."))
+        log.append(("DEBUG", str(err)))
+    except (IOError, ValueError, KeyError) as err:
+        log.append(("ERROR", "Download failed."))
+        log.append(("DEBUG", str(err)))
+
+    log.append(("INFO", "    Download finished."))
+    return success
+
+
+def _unzip_file(spec, build, pid, log):
+    """Unzip downloaded source file.
+
+    :param spec: Specification read form the specification file.
+    :param build: Information about the build.
+    :param log: List of log messages.
+    :type spec: Specification
+    :type build: dict
+    :type log: list of tuples (severity, msg)
+    :returns: True if the download was successful, otherwise False.
+    :rtype: bool
+    """
+
+    data_file = spec.input["extract"]
+    file_name = build["file-name"]
+    directory = spec.environment["paths"]["DIR[WORKING,DATA]"]
+    tmp_dir = join(directory, str(pid))
+    try:
+        mkdir(tmp_dir)
+    except OSError:
+        pass
+    new_name = "{0}{1}{2}".format(file_name.rsplit('.')[-2],
+                                  SEPARATOR,
+                                  data_file.split("/")[-1])
+
+    log.append(("INFO", "    Unzipping: '{0}' from '{1}'.".
+                format(data_file, file_name)))
+    try:
+        with ZipFile(file_name, 'r') as zip_file:
+            zip_file.extract(data_file, tmp_dir)
+        log.append(("INFO", "    Renaming the file '{0}' to '{1}'".
+                    format(join(tmp_dir, data_file), new_name)))
+        rename(join(tmp_dir, data_file), new_name)
+        build["file-name"] = new_name
+        return True
+    except (BadZipfile, RuntimeError) as err:
+        log.append(("ERROR", "Failed to unzip the file '{0}': {1}.".
+                    format(file_name, str(err))))
+        return False
+    except OSError as err:
+        log.append(("ERROR", "Failed to rename the file '{0}': {1}.".
+                    format(data_file, str(err))))
+        return False
+
+
+def download_and_unzip_data_file(spec, job, build, pid, log):
+    """Download and unzip a source file.
+
+    :param spec: Specification read form the specification file.
+    :param job: Name of the Jenkins job.
+    :param build: Information about the build.
+    :param pid: PID of the process executing this method.
+    :param log: List of log messages.
+    :type spec: Specification
+    :type job: str
+    :type build: dict
+    :type pid: int
+    :type log: list of tuples (severity, msg)
+    :returns: True if the download was successful, otherwise False.
+    :rtype: bool
+    """
+
+    if job.startswith("csit-"):
+        if spec.input["file-name"].endswith(".zip"):
+            url = spec.environment["urls"]["URL[JENKINS,CSIT]"]
+        elif spec.input["file-name"].endswith(".gz"):
+            url = spec.environment["urls"]["URL[NEXUS,LOG]"]
+        else:
+            log.append(("ERROR", "Not supported file format."))
+            return False
+    elif job.startswith("hc2vpp-"):
+        url = spec.environment["urls"]["URL[JENKINS,HC]"]
+    else:
+        raise PresentationError("No url defined for the job '{}'.".
+                                format(job))
+    file_name = spec.input["file-name"]
+    full_name = spec.input["download-path"]. \
+        format(job=job, build=build["build"], filename=file_name)
+    url = "{0}/{1}".format(url, full_name)
+    new_name = join(spec.environment["paths"]["DIR[WORKING,DATA]"],
+                    "{job}{sep}{build}{sep}{name}".
+                    format(job=job, sep=SEPARATOR, build=build["build"],
+                           name=file_name))
+    # Download the file from the defined source (Jenkins, logs.fd.io):
+    success = _download_file(url, new_name, log)
+
+    if success and new_name.endswith(".zip"):
+        if not is_zipfile(new_name):
+            success = False
+
+    # If not successful, download from docs.fd.io:
+    if not success:
+        log.append(("INFO", "    Trying to download from https://docs.fd.io:"))
+        release = re.search(REGEX_RELEASE, job).group(2)
+        for rls in (release, "master"):
+            nexus_file_name = "{job}{sep}{build}{sep}{name}". \
+                format(job=job, sep=SEPARATOR, build=build["build"],
+                       name=file_name)
+            try:
+                rls = "rls{0}".format(int(rls))
+            except ValueError:
+                pass
+            url = "{url}/{release}/{dir}/{file}". \
+                format(url=spec.environment["urls"]["URL[NEXUS]"],
+                       release=rls,
+                       dir=spec.environment["urls"]["DIR[NEXUS]"],
+                       file=nexus_file_name)
+            success = _download_file(url, new_name, log)
+            if success:
+                break
+
+    if success:
+        build["file-name"] = new_name
+    else:
+        return False
+
+    if spec.input["file-name"].endswith(".gz"):
+        if "docs.fd.io" in url:
+            execute_command("gzip --decompress --keep --force {0}".
+                            format(new_name))
+        else:
+            rename(new_name, new_name[:-3])
+            execute_command("gzip --keep {0}".format(new_name[:-3]))
+        build["file-name"] = new_name[:-3]
+
+    if new_name.endswith(".zip"):
+        if is_zipfile(new_name):
+            return _unzip_file(spec, build, pid, log)
+        else:
+            log.append(("ERROR",
+                        "Zip file '{0}' is corrupted.".format(new_name)))
+            return False
+    else:
+        return True
diff --git a/resources/tools/presentation/new/input_data_parser.py b/resources/tools/presentation/new/input_data_parser.py
new file mode 100644 (file)
index 0000000..beec34c
--- /dev/null
@@ -0,0 +1,1093 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Data pre-processing
+
+- extract data from output.xml files generated by Jenkins jobs and store in
+  pandas' Series,
+- provide access to the data.
+"""
+
+import multiprocessing
+import os
+import re
+import pandas as pd
+import logging
+
+from robot.api import ExecutionResult, ResultVisitor
+from robot import errors
+from collections import OrderedDict
+from string import replace
+from os import remove
+
+from input_data_files import download_and_unzip_data_file
+from utils import Worker
+
+
+class ExecutionChecker(ResultVisitor):
+    """Class to traverse through the test suite structure.
+
+    The functionality implemented in this class generates a json structure:
+
+    Performance tests:
+
+    {
+        "metadata": {  # Optional
+            "version": "VPP version",
+            "job": "Jenkins job name",
+            "build": "Information about the build"
+        },
+        "suites": {
+            "Suite name 1": {
+                "doc": "Suite 1 documentation",
+                "parent": "Suite 1 parent",
+                "level": "Level of the suite in the suite hierarchy"
+            }
+            "Suite name N": {
+                "doc": "Suite N documentation",
+                "parent": "Suite 2 parent",
+                "level": "Level of the suite in the suite hierarchy"
+            }
+        }
+        "tests": {
+            "ID": {
+                "name": "Test name",
+                "parent": "Name of the parent of the test",
+                "doc": "Test documentation"
+                "msg": "Test message"
+                "tags": ["tag 1", "tag 2", "tag n"],
+                "type": "PDR" | "NDR",
+                "throughput": {
+                    "value": int,
+                    "unit": "pps" | "bps" | "percentage"
+                },
+                "latency": {
+                    "direction1": {
+                        "100": {
+                            "min": int,
+                            "avg": int,
+                            "max": int
+                        },
+                        "50": {  # Only for NDR
+                            "min": int,
+                            "avg": int,
+                            "max": int
+                        },
+                        "10": {  # Only for NDR
+                            "min": int,
+                            "avg": int,
+                            "max": int
+                        }
+                    },
+                    "direction2": {
+                        "100": {
+                            "min": int,
+                            "avg": int,
+                            "max": int
+                        },
+                        "50": {  # Only for NDR
+                            "min": int,
+                            "avg": int,
+                            "max": int
+                        },
+                        "10": {  # Only for NDR
+                            "min": int,
+                            "avg": int,
+                            "max": int
+                        }
+                    }
+                },
+                "lossTolerance": "lossTolerance",  # Only for PDR
+                "vat-history": "DUT1 and DUT2 VAT History"
+                },
+                "show-run": "Show Run"
+            },
+            "ID" {
+                # next test
+            }
+        }
+    }
+
+    Functional tests:
+
+
+    {
+        "metadata": {  # Optional
+            "version": "VPP version",
+            "job": "Jenkins job name",
+            "build": "Information about the build"
+        },
+        "suites": {
+            "Suite name 1": {
+                "doc": "Suite 1 documentation",
+                "parent": "Suite 1 parent",
+                "level": "Level of the suite in the suite hierarchy"
+            }
+            "Suite name N": {
+                "doc": "Suite N documentation",
+                "parent": "Suite 2 parent",
+                "level": "Level of the suite in the suite hierarchy"
+            }
+        }
+        "tests": {
+            "ID": {
+                "name": "Test name",
+                "parent": "Name of the parent of the test",
+                "doc": "Test documentation"
+                "msg": "Test message"
+                "tags": ["tag 1", "tag 2", "tag n"],
+                "vat-history": "DUT1 and DUT2 VAT History"
+                "show-run": "Show Run"
+                "status": "PASS" | "FAIL"
+            },
+            "ID" {
+                # next test
+            }
+        }
+    }
+
+    .. note:: ID is the lowercase full path to the test.
+    """
+
+    REGEX_RATE = re.compile(r'^[\D\d]*FINAL_RATE:\s(\d+\.\d+)\s(\w+)')
+
+    REGEX_LAT_NDR = re.compile(r'^[\D\d]*'
+                               r'LAT_\d+%NDR:\s\[\'(-?\d+\/-?\d+/-?\d+)\','
+                               r'\s\'(-?\d+/-?\d+/-?\d+)\'\]\s\n'
+                               r'LAT_\d+%NDR:\s\[\'(-?\d+/-?\d+/-?\d+)\','
+                               r'\s\'(-?\d+/-?\d+/-?\d+)\'\]\s\n'
+                               r'LAT_\d+%NDR:\s\[\'(-?\d+/-?\d+/-?\d+)\','
+                               r'\s\'(-?\d+/-?\d+/-?\d+)\'\]')
+
+    REGEX_LAT_PDR = re.compile(r'^[\D\d]*'
+                               r'LAT_\d+%PDR:\s\[\'(-?\d+/-?\d+/-?\d+)\','
+                               r'\s\'(-?\d+/-?\d+/-?\d+)\'\][\D\d]*')
+
+    REGEX_TOLERANCE = re.compile(r'^[\D\d]*LOSS_ACCEPTANCE:\s(\d*\.\d*)\s'
+                                 r'[\D\d]*')
+
+    REGEX_VERSION = re.compile(r"(return STDOUT Version:\s*)(.*)")
+
+    REGEX_TCP = re.compile(r'Total\s(rps|cps|throughput):\s([0-9]*).*$')
+
+    REGEX_MRR = re.compile(r'MaxReceivedRate_Results\s\[pkts/(\d*)sec\]:\s'
+                           r'tx\s(\d*),\srx\s(\d*)')
+
+    def __init__(self, metadata):
+        """Initialisation.
+
+        :param metadata: Key-value pairs to be included in "metadata" part of
+        JSON structure.
+        :type metadata: dict
+        """
+
+        # Type of message to parse out from the test messages
+        self._msg_type = None
+
+        # VPP version
+        self._version = None
+
+        # Number of VAT History messages found:
+        # 0 - no message
+        # 1 - VAT History of DUT1
+        # 2 - VAT History of DUT2
+        self._lookup_kw_nr = 0
+        self._vat_history_lookup_nr = 0
+
+        # Number of Show Running messages found
+        # 0 - no message
+        # 1 - Show run message found
+        self._show_run_lookup_nr = 0
+
+        # Test ID of currently processed test- the lowercase full path to the
+        # test
+        self._test_ID = None
+
+        # The main data structure
+        self._data = {
+            "metadata": OrderedDict(),
+            "suites": OrderedDict(),
+            "tests": OrderedDict()
+        }
+
+        # Save the provided metadata
+        for key, val in metadata.items():
+            self._data["metadata"][key] = val
+
+        # Dictionary defining the methods used to parse different types of
+        # messages
+        self.parse_msg = {
+            "setup-version": self._get_version,
+            "teardown-vat-history": self._get_vat_history,
+            "test-show-runtime": self._get_show_run
+        }
+
+    @property
+    def data(self):
+        """Getter - Data parsed from the XML file.
+
+        :returns: Data parsed from the XML file.
+        :rtype: dict
+        """
+        return self._data
+
+    def _get_version(self, msg):
+        """Called when extraction of VPP version is required.
+
+        :param msg: Message to process.
+        :type msg: Message
+        :returns: Nothing.
+        """
+
+        if msg.message.count("return STDOUT Version:"):
+            self._version = str(re.search(self.REGEX_VERSION, msg.message).
+                                group(2))
+            self._data["metadata"]["version"] = self._version
+            self._data["metadata"]["generated"] = msg.timestamp
+            self._msg_type = None
+
+    def _get_vat_history(self, msg):
+        """Called when extraction of VAT command history is required.
+
+        :param msg: Message to process.
+        :type msg: Message
+        :returns: Nothing.
+        """
+        if msg.message.count("VAT command history:"):
+            self._vat_history_lookup_nr += 1
+            if self._vat_history_lookup_nr == 1:
+                self._data["tests"][self._test_ID]["vat-history"] = str()
+            else:
+                self._msg_type = None
+            text = re.sub("[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3} "
+                          "VAT command history:", "", msg.message, count=1). \
+                replace("\n\n", "\n").replace('\n', ' |br| ').\
+                replace('\r', '').replace('"', "'")
+
+            self._data["tests"][self._test_ID]["vat-history"] += " |br| "
+            self._data["tests"][self._test_ID]["vat-history"] += \
+                "**DUT" + str(self._vat_history_lookup_nr) + ":** " + text
+
+    def _get_show_run(self, msg):
+        """Called when extraction of VPP operational data (output of CLI command
+        Show Runtime) is required.
+
+        :param msg: Message to process.
+        :type msg: Message
+        :returns: Nothing.
+        """
+        if msg.message.count("return STDOUT Thread "):
+            self._show_run_lookup_nr += 1
+            if self._lookup_kw_nr == 1 and self._show_run_lookup_nr == 1:
+                self._data["tests"][self._test_ID]["show-run"] = str()
+            if self._lookup_kw_nr > 1:
+                self._msg_type = None
+            if self._show_run_lookup_nr == 1:
+                text = msg.message.replace("vat# ", "").\
+                    replace("return STDOUT ", "").replace("\n\n", "\n").\
+                    replace('\n', ' |br| ').\
+                    replace('\r', '').replace('"', "'")
+                try:
+                    self._data["tests"][self._test_ID]["show-run"] += " |br| "
+                    self._data["tests"][self._test_ID]["show-run"] += \
+                        "**DUT" + str(self._lookup_kw_nr) + ":** |br| " + text
+                except KeyError:
+                    pass
+
+    def _get_latency(self, msg, test_type):
+        """Get the latency data from the test message.
+
+        :param msg: Message to be parsed.
+        :param test_type: Type of the test - NDR or PDR.
+        :type msg: str
+        :type test_type: str
+        :returns: Latencies parsed from the message.
+        :rtype: dict
+        """
+
+        if test_type == "NDR":
+            groups = re.search(self.REGEX_LAT_NDR, msg)
+            groups_range = range(1, 7)
+        elif test_type == "PDR":
+            groups = re.search(self.REGEX_LAT_PDR, msg)
+            groups_range = range(1, 3)
+        else:
+            return {}
+
+        latencies = list()
+        for idx in groups_range:
+            try:
+                lat = [int(item) for item in str(groups.group(idx)).split('/')]
+            except (AttributeError, ValueError):
+                lat = [-1, -1, -1]
+            latencies.append(lat)
+
+        keys = ("min", "avg", "max")
+        latency = {
+            "direction1": {
+            },
+            "direction2": {
+            }
+        }
+
+        latency["direction1"]["100"] = dict(zip(keys, latencies[0]))
+        latency["direction2"]["100"] = dict(zip(keys, latencies[1]))
+        if test_type == "NDR":
+            latency["direction1"]["50"] = dict(zip(keys, latencies[2]))
+            latency["direction2"]["50"] = dict(zip(keys, latencies[3]))
+            latency["direction1"]["10"] = dict(zip(keys, latencies[4]))
+            latency["direction2"]["10"] = dict(zip(keys, latencies[5]))
+
+        return latency
+
+    def visit_suite(self, suite):
+        """Implements traversing through the suite and its direct children.
+
+        :param suite: Suite to process.
+        :type suite: Suite
+        :returns: Nothing.
+        """
+        if self.start_suite(suite) is not False:
+            suite.suites.visit(self)
+            suite.tests.visit(self)
+            self.end_suite(suite)
+
+    def start_suite(self, suite):
+        """Called when suite starts.
+
+        :param suite: Suite to process.
+        :type suite: Suite
+        :returns: Nothing.
+        """
+
+        try:
+            parent_name = suite.parent.name
+        except AttributeError:
+            return
+
+        doc_str = suite.doc.replace('"', "'").replace('\n', ' ').\
+            replace('\r', '').replace('*[', ' |br| *[').replace("*", "**")
+        doc_str = replace(doc_str, ' |br| *[', '*[', maxreplace=1)
+
+        self._data["suites"][suite.longname.lower().replace('"', "'").
+            replace(" ", "_")] = {
+                "name": suite.name.lower(),
+                "doc": doc_str,
+                "parent": parent_name,
+                "level": len(suite.longname.split("."))
+            }
+
+        suite.keywords.visit(self)
+
+    def end_suite(self, suite):
+        """Called when suite ends.
+
+        :param suite: Suite to process.
+        :type suite: Suite
+        :returns: Nothing.
+        """
+        pass
+
+    def visit_test(self, test):
+        """Implements traversing through the test.
+
+        :param test: Test to process.
+        :type test: Test
+        :returns: Nothing.
+        """
+        if self.start_test(test) is not False:
+            test.keywords.visit(self)
+            self.end_test(test)
+
+    def start_test(self, test):
+        """Called when test starts.
+
+        :param test: Test to process.
+        :type test: Test
+        :returns: Nothing.
+        """
+
+        tags = [str(tag) for tag in test.tags]
+        test_result = dict()
+        test_result["name"] = test.name.lower()
+        test_result["parent"] = test.parent.name.lower()
+        test_result["tags"] = tags
+        doc_str = test.doc.replace('"', "'").replace('\n', ' '). \
+            replace('\r', '').replace('[', ' |br| [')
+        test_result["doc"] = replace(doc_str, ' |br| [', '[', maxreplace=1)
+        test_result["msg"] = test.message.replace('\n', ' |br| '). \
+            replace('\r', '').replace('"', "'")
+        if test.status == "PASS" and ("NDRPDRDISC" in tags or
+                                      "TCP" in tags or
+                                      "MRR" in tags):
+            if "NDRDISC" in tags:
+                test_type = "NDR"
+            elif "PDRDISC" in tags:
+                test_type = "PDR"
+            elif "TCP" in tags:
+                test_type = "TCP"
+            elif "MRR" in tags:
+                test_type = "MRR"
+            else:
+                return
+
+            test_result["type"] = test_type
+
+            if test_type in ("NDR", "PDR"):
+                try:
+                    rate_value = str(re.search(
+                        self.REGEX_RATE, test.message).group(1))
+                except AttributeError:
+                    rate_value = "-1"
+                try:
+                    rate_unit = str(re.search(
+                        self.REGEX_RATE, test.message).group(2))
+                except AttributeError:
+                    rate_unit = "-1"
+
+                test_result["throughput"] = dict()
+                test_result["throughput"]["value"] = \
+                    int(rate_value.split('.')[0])
+                test_result["throughput"]["unit"] = rate_unit
+                test_result["latency"] = \
+                    self._get_latency(test.message, test_type)
+                if test_type == "PDR":
+                    test_result["lossTolerance"] = str(re.search(
+                        self.REGEX_TOLERANCE, test.message).group(1))
+
+            elif test_type in ("TCP", ):
+                groups = re.search(self.REGEX_TCP, test.message)
+                test_result["result"] = dict()
+                test_result["result"]["value"] = int(groups.group(2))
+                test_result["result"]["unit"] = groups.group(1)
+            elif test_type in ("MRR", ):
+                groups = re.search(self.REGEX_MRR, test.message)
+                test_result["result"] = dict()
+                test_result["result"]["duration"] = int(groups.group(1))
+                test_result["result"]["tx"] = int(groups.group(2))
+                test_result["result"]["rx"] = int(groups.group(3))
+                test_result["result"]["throughput"] = int(
+                    test_result["result"]["rx"] /
+                    test_result["result"]["duration"])
+        else:
+            test_result["status"] = test.status
+
+        self._test_ID = test.longname.lower()
+        self._data["tests"][self._test_ID] = test_result
+
+    def end_test(self, test):
+        """Called when test ends.
+
+        :param test: Test to process.
+        :type test: Test
+        :returns: Nothing.
+        """
+        pass
+
+    def visit_keyword(self, keyword):
+        """Implements traversing through the keyword and its child keywords.
+
+        :param keyword: Keyword to process.
+        :type keyword: Keyword
+        :returns: Nothing.
+        """
+        if self.start_keyword(keyword) is not False:
+            self.end_keyword(keyword)
+
+    def start_keyword(self, keyword):
+        """Called when keyword starts. Default implementation does nothing.
+
+        :param keyword: Keyword to process.
+        :type keyword: Keyword
+        :returns: Nothing.
+        """
+        try:
+            if keyword.type == "setup":
+                self.visit_setup_kw(keyword)
+            elif keyword.type == "teardown":
+                self._lookup_kw_nr = 0
+                self.visit_teardown_kw(keyword)
+            else:
+                self._lookup_kw_nr = 0
+                self.visit_test_kw(keyword)
+        except AttributeError:
+            pass
+
+    def end_keyword(self, keyword):
+        """Called when keyword ends. Default implementation does nothing.
+
+        :param keyword: Keyword to process.
+        :type keyword: Keyword
+        :returns: Nothing.
+        """
+        pass
+
+    def visit_test_kw(self, test_kw):
+        """Implements traversing through the test keyword and its child
+        keywords.
+
+        :param test_kw: Keyword to process.
+        :type test_kw: Keyword
+        :returns: Nothing.
+        """
+        for keyword in test_kw.keywords:
+            if self.start_test_kw(keyword) is not False:
+                self.visit_test_kw(keyword)
+                self.end_test_kw(keyword)
+
+    def start_test_kw(self, test_kw):
+        """Called when test keyword starts. Default implementation does
+        nothing.
+
+        :param test_kw: Keyword to process.
+        :type test_kw: Keyword
+        :returns: Nothing.
+        """
+        if test_kw.name.count("Show Runtime Counters On All Duts"):
+            self._lookup_kw_nr += 1
+            self._show_run_lookup_nr = 0
+            self._msg_type = "test-show-runtime"
+            test_kw.messages.visit(self)
+
+    def end_test_kw(self, test_kw):
+        """Called when keyword ends. Default implementation does nothing.
+
+        :param test_kw: Keyword to process.
+        :type test_kw: Keyword
+        :returns: Nothing.
+        """
+        pass
+
+    def visit_setup_kw(self, setup_kw):
+        """Implements traversing through the teardown keyword and its child
+        keywords.
+
+        :param setup_kw: Keyword to process.
+        :type setup_kw: Keyword
+        :returns: Nothing.
+        """
+        for keyword in setup_kw.keywords:
+            if self.start_setup_kw(keyword) is not False:
+                self.visit_setup_kw(keyword)
+                self.end_setup_kw(keyword)
+
+    def start_setup_kw(self, setup_kw):
+        """Called when teardown keyword starts. Default implementation does
+        nothing.
+
+        :param setup_kw: Keyword to process.
+        :type setup_kw: Keyword
+        :returns: Nothing.
+        """
+        if setup_kw.name.count("Show Vpp Version On All Duts") \
+                and not self._version:
+            self._msg_type = "setup-version"
+            setup_kw.messages.visit(self)
+
+    def end_setup_kw(self, setup_kw):
+        """Called when keyword ends. Default implementation does nothing.
+
+        :param setup_kw: Keyword to process.
+        :type setup_kw: Keyword
+        :returns: Nothing.
+        """
+        pass
+
+    def visit_teardown_kw(self, teardown_kw):
+        """Implements traversing through the teardown keyword and its child
+        keywords.
+
+        :param teardown_kw: Keyword to process.
+        :type teardown_kw: Keyword
+        :returns: Nothing.
+        """
+        for keyword in teardown_kw.keywords:
+            if self.start_teardown_kw(keyword) is not False:
+                self.visit_teardown_kw(keyword)
+                self.end_teardown_kw(keyword)
+
+    def start_teardown_kw(self, teardown_kw):
+        """Called when teardown keyword starts. Default implementation does
+        nothing.
+
+        :param teardown_kw: Keyword to process.
+        :type teardown_kw: Keyword
+        :returns: Nothing.
+        """
+
+        if teardown_kw.name.count("Show Vat History On All Duts"):
+            self._vat_history_lookup_nr = 0
+            self._msg_type = "teardown-vat-history"
+            teardown_kw.messages.visit(self)
+
+    def end_teardown_kw(self, teardown_kw):
+        """Called when keyword ends. Default implementation does nothing.
+
+        :param teardown_kw: Keyword to process.
+        :type teardown_kw: Keyword
+        :returns: Nothing.
+        """
+        pass
+
+    def visit_message(self, msg):
+        """Implements visiting the message.
+
+        :param msg: Message to process.
+        :type msg: Message
+        :returns: Nothing.
+        """
+        if self.start_message(msg) is not False:
+            self.end_message(msg)
+
+    def start_message(self, msg):
+        """Called when message starts. Get required information from messages:
+        - VPP version.
+
+        :param msg: Message to process.
+        :type msg: Message
+        :returns: Nothing.
+        """
+
+        if self._msg_type:
+            self.parse_msg[self._msg_type](msg)
+
+    def end_message(self, msg):
+        """Called when message ends. Default implementation does nothing.
+
+        :param msg: Message to process.
+        :type msg: Message
+        :returns: Nothing.
+        """
+        pass
+
+
+class InputData(object):
+    """Input data
+
+    The data is extracted from output.xml files generated by Jenkins jobs and
+    stored in pandas' DataFrames.
+
+    The data structure:
+    - job name
+      - build number
+        - metadata
+          - job
+          - build
+          - vpp version
+        - suites
+        - tests
+          - ID: test data (as described in ExecutionChecker documentation)
+    """
+
+    def __init__(self, spec):
+        """Initialization.
+
+        :param spec: Specification.
+        :type spec: Specification
+        """
+
+        # Specification:
+        self._cfg = spec
+
+        # Data store:
+        self._input_data = pd.Series()
+
+    @property
+    def data(self):
+        """Getter - Input data.
+
+        :returns: Input data
+        :rtype: pandas.Series
+        """
+        return self._input_data
+
+    def metadata(self, job, build):
+        """Getter - metadata
+
+        :param job: Job which metadata we want.
+        :param build: Build which metadata we want.
+        :type job: str
+        :type build: str
+        :returns: Metadata
+        :rtype: pandas.Series
+        """
+
+        return self.data[job][build]["metadata"]
+
+    def suites(self, job, build):
+        """Getter - suites
+
+        :param job: Job which suites we want.
+        :param build: Build which suites we want.
+        :type job: str
+        :type build: str
+        :returns: Suites.
+        :rtype: pandas.Series
+        """
+
+        return self.data[job][str(build)]["suites"]
+
+    def tests(self, job, build):
+        """Getter - tests
+
+        :param job: Job which tests we want.
+        :param build: Build which tests we want.
+        :type job: str
+        :type build: str
+        :returns: Tests.
+        :rtype: pandas.Series
+        """
+
+        return self.data[job][build]["tests"]
+
+    @staticmethod
+    def _parse_tests(job, build, log):
+        """Process data from robot output.xml file and return JSON structured
+        data.
+
+        :param job: The name of job which build output data will be processed.
+        :param build: The build which output data will be processed.
+        :param log: List of log messages.
+        :type job: str
+        :type build: dict
+        :type log: list of tuples (severity, msg)
+        :returns: JSON data structure.
+        :rtype: dict
+        """
+
+        metadata = {
+            "job": job,
+            "build": build
+        }
+
+        with open(build["file-name"], 'r') as data_file:
+            try:
+                result = ExecutionResult(data_file)
+            except errors.DataError as err:
+                log.append(("ERROR", "Error occurred while parsing output.xml: "
+                                     "{0}".format(err)))
+                return None
+        checker = ExecutionChecker(metadata)
+        result.visit(checker)
+
+        return checker.data
+
+    def _download_and_parse_build(self, pid, data_queue, job, build, repeat):
+        """Download and parse the input data file.
+
+        :param pid: PID of the process executing this method.
+        :param data_queue: Shared memory between processes. Queue which keeps
+            the result data. This data is then read by the main process and used
+            in further processing.
+        :param job: Name of the Jenkins job which generated the processed input
+            file.
+        :param build: Information about the Jenkins build which generated the
+            processed input file.
+        :param repeat: Repeat the download specified number of times if not
+            successful.
+        :type pid: int
+        :type data_queue: multiprocessing.Manager().Queue()
+        :type job: str
+        :type build: dict
+        :type repeat: int
+        """
+
+        logs = list()
+
+        logging.info("  Processing the job/build: {0}: {1}".
+                     format(job, build["build"]))
+
+        logs.append(("INFO", "  Processing the job/build: {0}: {1}".
+                     format(job, build["build"])))
+
+        state = "failed"
+        success = False
+        data = None
+        do_repeat = repeat
+        while do_repeat:
+            success = download_and_unzip_data_file(self._cfg, job, build, pid,
+                                                   logs)
+            if success:
+                break
+            do_repeat -= 1
+        if not success:
+            logs.append(("ERROR", "It is not possible to download the input "
+                                  "data file from the job '{job}', build "
+                                  "'{build}', or it is damaged. Skipped.".
+                         format(job=job, build=build["build"])))
+        if success:
+            logs.append(("INFO", "  Processing data from the build '{0}' ...".
+                         format(build["build"])))
+            data = InputData._parse_tests(job, build, logs)
+            if data is None:
+                logs.append(("ERROR", "Input data file from the job '{job}', "
+                                      "build '{build}' is damaged. Skipped.".
+                             format(job=job, build=build["build"])))
+            else:
+                state = "processed"
+
+            try:
+                remove(build["file-name"])
+            except OSError as err:
+                logs.append(("ERROR", "Cannot remove the file '{0}': {1}".
+                             format(build["file-name"], err)))
+        logs.append(("INFO", "  Done."))
+
+        result = {
+            "data": data,
+            "state": state,
+            "job": job,
+            "build": build,
+            "logs": logs
+        }
+        data_queue.put(result)
+
+    def download_and_parse_data(self, repeat=1):
+        """Download the input data files, parse input data from input files and
+        store in pandas' Series.
+
+        :param repeat: Repeat the download specified number of times if not
+            successful.
+        :type repeat: int
+        """
+
+        logging.info("Downloading and parsing input files ...")
+
+        work_queue = multiprocessing.JoinableQueue()
+        manager = multiprocessing.Manager()
+        data_queue = manager.Queue()
+        cpus = multiprocessing.cpu_count()
+
+        workers = list()
+        for cpu in range(cpus):
+            worker = Worker(work_queue,
+                            data_queue,
+                            self._download_and_parse_build)
+            worker.daemon = True
+            worker.start()
+            workers.append(worker)
+            os.system("taskset -p -c {0} {1} > /dev/null 2>&1".
+                      format(cpu, worker.pid))
+
+        for job, builds in self._cfg.builds.items():
+            for build in builds:
+                work_queue.put((job, build, repeat))
+
+        work_queue.join()
+
+        logging.info("Done.")
+
+        while not data_queue.empty():
+            result = data_queue.get()
+
+            job = result["job"]
+            build_nr = result["build"]["build"]
+
+            if result["data"]:
+                data = result["data"]
+                build_data = pd.Series({
+                    "metadata": pd.Series(data["metadata"].values(),
+                                          index=data["metadata"].keys()),
+                    "suites": pd.Series(data["suites"].values(),
+                                        index=data["suites"].keys()),
+                    "tests": pd.Series(data["tests"].values(),
+                                       index=data["tests"].keys())})
+
+                if self._input_data.get(job, None) is None:
+                    self._input_data[job] = pd.Series()
+                self._input_data[job][str(build_nr)] = build_data
+
+                self._cfg.set_input_file_name(job, build_nr,
+                                              result["build"]["file-name"])
+
+            self._cfg.set_input_state(job, build_nr, result["state"])
+
+            for item in result["logs"]:
+                if item[0] == "INFO":
+                    logging.info(item[1])
+                elif item[0] == "ERROR":
+                    logging.error(item[1])
+                elif item[0] == "DEBUG":
+                    logging.debug(item[1])
+                elif item[0] == "CRITICAL":
+                    logging.critical(item[1])
+                elif item[0] == "WARNING":
+                    logging.warning(item[1])
+
+        del data_queue
+
+        # Terminate all workers
+        for worker in workers:
+            worker.terminate()
+            worker.join()
+
+        logging.info("Done.")
+
+    @staticmethod
+    def _end_of_tag(tag_filter, start=0, closer="'"):
+        """Return the index of character in the string which is the end of tag.
+
+        :param tag_filter: The string where the end of tag is being searched.
+        :param start: The index where the searching is stated.
+        :param closer: The character which is the tag closer.
+        :type tag_filter: str
+        :type start: int
+        :type closer: str
+        :returns: The index of the tag closer.
+        :rtype: int
+        """
+
+        try:
+            idx_opener = tag_filter.index(closer, start)
+            return tag_filter.index(closer, idx_opener + 1)
+        except ValueError:
+            return None
+
+    @staticmethod
+    def _condition(tag_filter):
+        """Create a conditional statement from the given tag filter.
+
+        :param tag_filter: Filter based on tags from the element specification.
+        :type tag_filter: str
+        :returns: Conditional statement which can be evaluated.
+        :rtype: str
+        """
+
+        index = 0
+        while True:
+            index = InputData._end_of_tag(tag_filter, index)
+            if index is None:
+                return tag_filter
+            index += 1
+            tag_filter = tag_filter[:index] + " in tags" + tag_filter[index:]
+
+    def filter_data(self, element, params=None, data_set="tests",
+                    continue_on_error=False):
+        """Filter required data from the given jobs and builds.
+
+        The output data structure is:
+
+        - job 1
+          - build 1
+            - test (suite) 1 ID:
+              - param 1
+              - param 2
+              ...
+              - param n
+            ...
+            - test (suite) n ID:
+            ...
+          ...
+          - build n
+        ...
+        - job n
+
+        :param element: Element which will use the filtered data.
+        :param params: Parameters which will be included in the output. If None,
+        all parameters are included.
+        :param data_set: The set of data to be filtered: tests, suites,
+        metadata.
+        :param continue_on_error: Continue if there is error while reading the
+        data. The Item will be empty then
+        :type element: pandas.Series
+        :type params: list
+        :type data_set: str
+        :type continue_on_error: bool
+        :returns: Filtered data.
+        :rtype pandas.Series
+        """
+
+        try:
+            if element["filter"] in ("all", "template"):
+                cond = "True"
+            else:
+                cond = InputData._condition(element["filter"])
+            logging.debug("   Filter: {0}".format(cond))
+        except KeyError:
+            logging.error("  No filter defined.")
+            return None
+
+        if params is None:
+            params = element.get("parameters", None)
+
+        data = pd.Series()
+        try:
+            for job, builds in element["data"].items():
+                data[job] = pd.Series()
+                for build in builds:
+                    data[job][str(build)] = pd.Series()
+                    try:
+                        data_iter = self.data[job][str(build)][data_set].\
+                            iteritems()
+                    except KeyError:
+                        if continue_on_error:
+                            continue
+                        else:
+                            return None
+                    for test_ID, test_data in data_iter:
+                        if eval(cond, {"tags": test_data.get("tags", "")}):
+                            data[job][str(build)][test_ID] = pd.Series()
+                            if params is None:
+                                for param, val in test_data.items():
+                                    data[job][str(build)][test_ID][param] = val
+                            else:
+                                for param in params:
+                                    try:
+                                        data[job][str(build)][test_ID][param] =\
+                                            test_data[param]
+                                    except KeyError:
+                                        data[job][str(build)][test_ID][param] =\
+                                            "No Data"
+            return data
+
+        except (KeyError, IndexError, ValueError) as err:
+            logging.error("   Missing mandatory parameter in the element "
+                          "specification: {0}".format(err))
+            return None
+        except AttributeError:
+            return None
+        except SyntaxError:
+            logging.error("   The filter '{0}' is not correct. Check if all "
+                          "tags are enclosed by apostrophes.".format(cond))
+            return None
+
+    @staticmethod
+    def merge_data(data):
+        """Merge data from more jobs and builds to a simple data structure.
+
+        The output data structure is:
+
+        - test (suite) 1 ID:
+          - param 1
+          - param 2
+          ...
+          - param n
+        ...
+        - test (suite) n ID:
+        ...
+
+        :param data: Data to merge.
+        :type data: pandas.Series
+        :returns: Merged data.
+        :rtype: pandas.Series
+        """
+
+        logging.info("    Merging data ...")
+
+        merged_data = pd.Series()
+        for _, builds in data.iteritems():
+            for _, item in builds.iteritems():
+                for ID, item_data in item.iteritems():
+                    merged_data[ID] = item_data
+
+        return merged_data
diff --git a/resources/tools/presentation/new/jumpavg/AbstractGroupClassifier.py b/resources/tools/presentation/new/jumpavg/AbstractGroupClassifier.py
new file mode 100644 (file)
index 0000000..26db758
--- /dev/null
@@ -0,0 +1,33 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from abc import ABCMeta, abstractmethod
+
+
+class AbstractGroupClassifier(object):
+
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def classify(self, values):
+        """Divide values into consecutive groups with metadata.
+
+        The metadata does not need to follow any specific rules,
+        although progression/regression/outlier description would be fine.
+
+        :param values: Sequence of runs to classify.
+        :type values: Iterable of float or of AvgStdevMetadata
+        :returns: Classified groups
+        :rtype: Iterable of RunGroup
+        """
+        pass
diff --git a/resources/tools/presentation/new/jumpavg/AbstractGroupMetadata.py b/resources/tools/presentation/new/jumpavg/AbstractGroupMetadata.py
new file mode 100644 (file)
index 0000000..6084db5
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from abc import ABCMeta, abstractmethod
+
+
+class AbstractGroupMetadata(object):
+
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def __str__(self):
+        """Return string with human readable description of the group.
+
+        :returns: Readable description.
+        :rtype: str
+        """
+        pass
+
+    @abstractmethod
+    def __repr__(self):
+        """Return string executable as Python constructor call.
+
+        :returns: Executable constructor call.
+        :rtype: str
+        """
+        pass
diff --git a/resources/tools/presentation/new/jumpavg/AvgStdevMetadata.py b/resources/tools/presentation/new/jumpavg/AvgStdevMetadata.py
new file mode 100644 (file)
index 0000000..bd7eca1
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class AvgStdevMetadata(object):
+    """Class for metadata specifying the average and standard deviation."""
+
+    def __init__(self, size=0, avg=0.0, stdev=0.0):
+        """Construct the metadata by setting the values needed.
+
+        The values are sanitized, so faulty callers to not cause math errors.
+
+        :param size: Number of values participating in this group.
+        :param avg: Population average of the participating sample values.
+        :param stdev: Population standard deviation of the sample values.
+        :type size: int
+        :type avg: float
+        :type stdev: float
+        """
+        self.size = size if size >= 0 else 0
+        self.avg = avg if size >= 1 else 0.0
+        self.stdev = stdev if size >= 2 else 0.0
+
+    def __str__(self):
+        """Return string with human readable description of the group.
+
+        :returns: Readable description.
+        :rtype: str
+        """
+        return "size={size} avg={avg} stdev={stdev}".format(
+            size=self.size, avg=self.avg, stdev=self.stdev)
+
+    def __repr__(self):
+        """Return string executable as Python constructor call.
+
+        :returns: Executable constructor call.
+        :rtype: str
+        """
+        return "AvgStdevMetadata(size={size},avg={avg},stdev={stdev})".format(
+            size=self.size, avg=self.avg, stdev=self.stdev)
diff --git a/resources/tools/presentation/new/jumpavg/AvgStdevMetadataFactory.py b/resources/tools/presentation/new/jumpavg/AvgStdevMetadataFactory.py
new file mode 100644 (file)
index 0000000..d7d0517
--- /dev/null
@@ -0,0 +1,49 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+
+from AvgStdevMetadata import AvgStdevMetadata
+
+
+class AvgStdevMetadataFactory(object):
+    """Class factory which creates avg,stdev metadata from data."""
+
+    @staticmethod
+    def from_data(values):
+        """Return new metadata object fitting the values.
+
+        :param values: Run values to be processed.
+        :type values: Iterable of float or of AvgStdevMetadata
+        :returns: The metadata matching the values.
+        :rtype: AvgStdevMetadata
+        """
+        sum_0 = 0
+        sum_1 = 0.0
+        sum_2 = 0.0
+        for value in values:
+            if isinstance(value, AvgStdevMetadata):
+                sum_0 += value.size
+                sum_1 += value.avg * value.size
+                sum_2 += value.stdev * value.stdev * value.size
+                sum_2 += value.avg * value.avg * value.size
+            else:  # The value is assumed to be float.
+                sum_0 += 1
+                sum_1 += value
+                sum_2 += value * value
+        if sum_0 < 1:
+            return AvgStdevMetadata()
+        avg = sum_1 / sum_0
+        stdev = math.sqrt(sum_2 / sum_0 - avg * avg)
+        ret_obj = AvgStdevMetadata(size=sum_0, avg=avg, stdev=stdev)
+        return ret_obj
diff --git a/resources/tools/presentation/new/jumpavg/BitCountingClassifier.py b/resources/tools/presentation/new/jumpavg/BitCountingClassifier.py
new file mode 100644 (file)
index 0000000..69b1d65
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from BitCountingGroup import BitCountingGroup
+from BitCountingGroupList import BitCountingGroupList
+from BitCountingMetadataFactory import BitCountingMetadataFactory
+from ClassifiedMetadataFactory import ClassifiedMetadataFactory
+
+
+class BitCountingClassifier(object):
+
+    @staticmethod
+    def classify(values):
+        """Return the values in groups of optimal bit count.
+
+        TODO: Could we return BitCountingGroupList and let caller process it?
+
+        :param values: Sequence of runs to classify.
+        :type values: Iterable of float or of AvgStdevMetadata
+        :returns: Classified group list.
+        :rtype: list of BitCountingGroup
+        """
+        max_value = BitCountingMetadataFactory.find_max_value(values)
+        factory = BitCountingMetadataFactory(max_value)
+        opened_at = []
+        closed_before = [BitCountingGroupList()]
+        for index, value in enumerate(values):
+            singleton = BitCountingGroup(factory, [value])
+            newly_opened = closed_before[index].with_group_appended(singleton)
+            opened_at.append(newly_opened)
+            record_group_list = newly_opened
+            for previous in range(index):
+                previous_opened_list = opened_at[previous]
+                still_opened = (
+                    previous_opened_list.with_value_added_to_last_group(value))
+                opened_at[previous] = still_opened
+                if still_opened.bits < record_group_list.bits:
+                    record_group_list = still_opened
+            closed_before.append(record_group_list)
+        partition = closed_before[-1]
+        previous_average = partition[0].metadata.avg
+        for group in partition:
+            if group.metadata.avg == previous_average:
+                group.metadata = ClassifiedMetadataFactory.with_classification(
+                    group.metadata, "normal")
+            elif group.metadata.avg < previous_average:
+                group.metadata = ClassifiedMetadataFactory.with_classification(
+                    group.metadata, "regression")
+            elif group.metadata.avg > previous_average:
+                group.metadata = ClassifiedMetadataFactory.with_classification(
+                    group.metadata, "progression")
+            previous_average = group.metadata.avg
+        return partition.group_list
diff --git a/resources/tools/presentation/new/jumpavg/BitCountingGroup.py b/resources/tools/presentation/new/jumpavg/BitCountingGroup.py
new file mode 100644 (file)
index 0000000..144f5a8
--- /dev/null
@@ -0,0 +1,43 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from RunGroup import RunGroup
+
+
+class BitCountingGroup(RunGroup):
+
+    def __init__(self, metadata_factory, values=[]):
+        """Create the group from metadata factory and values.
+
+        :param metadata_factory: Factory object to create metadata with.
+        :param values: The runs belonging to this group.
+        :type metadata_factory: BitCountingMetadataFactory
+        :type values: Iterable of float or of AvgStdevMetadata
+        """
+        self.metadata_factory = metadata_factory
+        metadata = metadata_factory.from_data(values)
+        super(BitCountingGroup, self).__init__(metadata, values)
+
+    def with_run_added(self, value):
+        """Create and return a new group with one more run that self.
+
+        :param value: The run value to add to the group.
+        :type value: float or od AvgStdevMetadata
+        :returns: New group with the run added.
+        :rtype: BitCountingGroup
+        """
+        values = list(self.values)
+        values.append(value)
+        return BitCountingGroup(self.metadata_factory, values)
+        # TODO: Is there a good way to save some computation
+        # by copy&updating the metadata incrementally?
diff --git a/resources/tools/presentation/new/jumpavg/BitCountingGroupList.py b/resources/tools/presentation/new/jumpavg/BitCountingGroupList.py
new file mode 100644 (file)
index 0000000..7da0656
--- /dev/null
@@ -0,0 +1,82 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from BitCountingGroup import BitCountingGroup
+from BitCountingMetadataFactory import BitCountingMetadataFactory
+
+
+class BitCountingGroupList(object):
+
+    def __init__(self, group_list=[], bits=None):
+        """Create a group list from given list of groups.
+
+        :param group_list: List of groups to compose this group.
+        :param bits: Bit count if known, else None.
+        :type group_list: list of BitCountingGroup
+        :type bits: float or None
+        """
+        self.group_list = group_list
+        if bits is not None:
+            self.bits = bits
+            return
+        bits = 0.0
+        for group in group_list:
+            bits += group.metadata.bits
+        self.bits = bits
+
+    def __getitem__(self, index):
+        """Return group at the index. This makes self iterable.
+
+        :param index: The position in the array of groups.
+        :type index: int
+        :returns: Group at the position.
+        :rtype: BitCountingGroup
+        """
+        return self.group_list[index]
+
+    def with_group_appended(self, group):
+        """Create and return new group list with given group more than self.
+
+        The group argument object is updated with derivative metadata.
+
+        :param group: Next group to be appended to the group list.
+        :type group: BitCountingGroup
+        :returns: New group list with added group.
+        :rtype: BitCountingGroupList
+        """
+        group_list = list(self.group_list)
+        if group_list:
+            last_group = group_list[-1]
+            factory = BitCountingMetadataFactory(
+                last_group.metadata_factory.max_value, last_group.metadata.avg)
+            group.metadata_factory = factory
+            group.metadata = factory.from_data(group.values)
+        group_list.append(group)
+        bits = self.bits + group.metadata.bits
+        return BitCountingGroupList(group_list, bits)
+
+    def with_value_added_to_last_group(self, value):
+        """Create and return new group list with value added to last group.
+
+        :param value: The run value to add to the last group.
+        :type value: float or od AvgStdevMetadata
+        :returns: New group list with the last group updated.
+        :rtype: BitCountingGroupList
+        """
+        last_group = self.group_list[-1]
+        bits_before = last_group.metadata.bits
+        last_group = last_group.with_run_added(value)
+        group_list = list(self.group_list)
+        group_list[-1] = last_group
+        bits = self.bits - bits_before + last_group.metadata.bits
+        return BitCountingGroupList(group_list, bits)
diff --git a/resources/tools/presentation/new/jumpavg/BitCountingMetadata.py b/resources/tools/presentation/new/jumpavg/BitCountingMetadata.py
new file mode 100644 (file)
index 0000000..67d1119
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+
+from AvgStdevMetadata import AvgStdevMetadata
+
+
+class BitCountingMetadata(AvgStdevMetadata):
+    """Class for metadata which includes information content."""
+
+    def __init__(self, max_value, size=0, avg=0.0, stdev=0.0, prev_avg=None):
+        """Construct the metadata by computing from the values needed.
+
+        The bit count is not real, as that would depend on numeric precision
+        (number of significant bits in values).
+        The difference is assumed to be constant per value,
+        which is consistent with Gauss distribution
+        (but not with floating point mechanic).
+        The hope is the difference will have
+        no real impact on the classification procedure.
+
+        :param max_value: Maximal expected value.
+            TODO: This might be more optimal,
+            but max-invariant algorithm will be nicer.
+        :param size: Number of values participating in this group.
+        :param avg: Population average of the participating sample values.
+        :param stdev: Population standard deviation of the sample values.
+        :param prev_avg: Population average of the previous group.
+            If None, no previous average is taken into account.
+            If not None, the given previous average is used to discourage
+            consecutive groups with similar averages
+            (opposite triangle distribution is assumed).
+        :type max_value: float
+        :type size: int
+        :type avg: float
+        :type stdev: float
+        :type prev_avg: float or None
+        """
+        super(BitCountingMetadata, self).__init__(size, avg, stdev)
+        self.max_value = max_value
+        self.prev_avg = prev_avg
+        self.bits = 0.0
+        if self.size < 1:
+            return
+        # Length of the sequence must be also counted in bits,
+        # otherwise the message would not be decodable.
+        # Model: probability of k samples is 1/k - 1/(k+1)
+        # == 1/k/(k+1)
+        self.bits += math.log(size * (size + 1), 2)
+        if prev_avg is None:
+            # Avg is considered to be uniformly distributed
+            # from zero to max_value.
+            self.bits += math.log(max_value + 1.0, 2)
+        else:
+            # Opposite triangle distribution with minimum.
+            self.bits += math.log(
+                max_value * (max_value + 1) / (abs(avg - prev_avg) + 1), 2)
+        if self.size < 2:
+            return
+        # Stdev is considered to be uniformly distributed
+        # from zero to max_value. That is quite a bad expectation,
+        # but resilient to negative samples etc.
+        self.bits += math.log(max_value + 1.0, 2)
+        # Now we know the samples lie on sphere in size-1 dimensions.
+        # So it is (size-2)-sphere, with radius^2 == stdev^2 * size.
+        # https://en.wikipedia.org/wiki/N-sphere
+        sphere_area_ln = math.log(2) + math.log(math.pi) * ((size - 1) / 2.0)
+        sphere_area_ln -= math.lgamma((size - 1) / 2.0)
+        sphere_area_ln += math.log(stdev + 1.0) * (size - 2)
+        sphere_area_ln += math.log(size) * ((size - 2) / 2.0)
+        self.bits += sphere_area_ln / math.log(2)
+
+    def __str__(self):
+        """Return string with human readable description of the group.
+
+        :returns: Readable description.
+        :rtype: str
+        """
+        return "size={size} avg={avg} stdev={stdev} bits={bits}".format(
+            size=self.size, avg=self.avg, stdev=self.stdev, bits=self.bits)
+
+    def __repr__(self):
+        """Return string executable as Python constructor call.
+
+        :returns: Executable constructor call.
+        :rtype: str
+        """
+        return ("BitCountingMetadata(max_value={max_value},size={size}," +
+                "avg={avg},stdev={stdev},prev_avg={prev_avg})").format(
+                    max_value=self.max_value, size=self.size, avg=self.avg,
+                    stdev=self.stdev, prev_avg=self.prev_avg)
diff --git a/resources/tools/presentation/new/jumpavg/BitCountingMetadataFactory.py b/resources/tools/presentation/new/jumpavg/BitCountingMetadataFactory.py
new file mode 100644 (file)
index 0000000..5a7b393
--- /dev/null
@@ -0,0 +1,80 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+
+from AvgStdevMetadata import AvgStdevMetadata
+from AvgStdevMetadataFactory import AvgStdevMetadataFactory
+from BitCountingMetadata import BitCountingMetadata
+
+
+class BitCountingMetadataFactory(object):
+    """Class for factory which creates bit counting metadata from data."""
+
+    @staticmethod
+    def find_max_value(values):
+        """Return the max value.
+
+        This is a separate helper method,
+        because the whole set of values is usually larger than in from_data().
+
+        :param values: Run values to be processed.
+        :type values: Iterable of float
+        :returns: 0.0 or the biggest value found.
+        :rtype: float
+        """
+        max_value = 0.0
+        for value in values:
+            if isinstance(value, AvgStdevMetadata):
+                value = value.avg
+            if value > max_value:
+                max_value = value
+        return max_value
+
+    def __init__(self, max_value, prev_avg=None):
+        """Construct the factory instance with given arguments.
+
+        :param max_value: Maximal expected value.
+        :param prev_avg: Population average of the previous group.
+            If None, no previous average is taken into account.
+            If not None, the given previous average is used to discourage
+            consecutive groups with similar averages
+            (opposite triangle distribution is assumed).
+        :type max_value: float
+        :type prev_avg: float or None
+        """
+        self.max_value = max_value
+        self.prev_avg = prev_avg
+
+    def from_avg_stdev_metadata(self, metadata):
+        """Return new metadata object by adding bits to existing metadata.
+
+        :param metadata: Metadata to count bits for.
+        :type metadata: AvgStdevMetadata
+        :returns: The metadata with bits counted.
+        :rtype: BitCountingMetadata
+        """
+        return BitCountingMetadata(
+            max_value=self.max_value, size=metadata.size,
+            avg=metadata.avg, stdev=metadata.stdev, prev_avg=self.prev_avg)
+
+    def from_data(self, values):
+        """Return new metadata object fitting the values.
+
+        :param values: Run values to be processed.
+        :type values: Iterable of float or of AvgStdevMetadata
+        :returns: The metadata matching the values.
+        :rtype: BitCountingMetadata
+        """
+        metadata = AvgStdevMetadataFactory.from_data(values)
+        return self.from_avg_stdev_metadata(metadata)
diff --git a/resources/tools/presentation/new/jumpavg/ClassifiedBitCountingMetadata.py b/resources/tools/presentation/new/jumpavg/ClassifiedBitCountingMetadata.py
new file mode 100644 (file)
index 0000000..9a7277b
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from BitCountingMetadata import BitCountingMetadata
+
+
+class ClassifiedBitCountingMetadata(BitCountingMetadata):
+    """Class for metadata which includes classification."""
+
+    def __init__(
+            self, max_value, size=0, avg=0.0, stdev=0.0, prev_avg=None,
+            classification=None):
+        """Delegate to ancestor constructors and set classification.
+
+        :param max_value: Maximal expected value.
+        :param size: Number of values participating in this group.
+        :param avg: Population average of the participating sample values.
+        :param stdev: Population standard deviation of the sample values.
+        :param prev_avg: Population average of the previous group.
+            If None, no previous average is taken into account.
+            If not None, the given previous average is used to discourage
+            consecutive groups with similar averages
+            (opposite triangle distribution is assumed).
+        :param classification: Arbitrary object classifying this group.
+        :type max_value: float
+        :type size: int
+        :type avg: float
+        :type stdev: float
+        :type prev_avg: float
+        :type classification: object
+        """
+        super(ClassifiedBitCountingMetadata, self).__init__(
+            max_value, size, avg, stdev, prev_avg)
+        self.classification = classification
+
+    def __str__(self):
+        """Return string with human readable description of the group.
+
+        :returns: Readable description.
+        :rtype: str
+        """
+        # str(super(...)) describes the proxy, not the proxied object.
+        super_str = super(ClassifiedBitCountingMetadata, self).__str__()
+        return super_str + " classification={classification}".format(
+            classification=self.classification)
+
+    def __repr__(self):
+        """Return string executable as Python constructor call.
+
+        :returns: Executable constructor call.
+        :rtype: str
+        """
+        return ("ClassifiedBitCountingMetadata(max_value={max_value}," +
+                "size={size},avg={avg},stdev={stdev},prev_avg={prev_avg}," +
+                "classification={cls})").format(
+                    max_value=self.max_value, size=self.size, avg=self.avg,
+                    stdev=self.stdev, prev_avg=self.prev_avg,
+                    cls=self.classification)
diff --git a/resources/tools/presentation/new/jumpavg/ClassifiedMetadataFactory.py b/resources/tools/presentation/new/jumpavg/ClassifiedMetadataFactory.py
new file mode 100644 (file)
index 0000000..39b157f
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+
+from ClassifiedBitCountingMetadata import ClassifiedBitCountingMetadata
+
+
+class ClassifiedMetadataFactory(object):
+    """Class for factory which adds classification to bit counting metadata."""
+
+    @staticmethod
+    def with_classification(metadata, classification):
+        """Return new metadata object with added classification.
+
+        TODO: Is there a way to add classification to any metadata,
+        without messing up constructors and __repr__()?
+
+        FIXME: Factories take raw resources. Find a name for the thing
+        which takes semi-finished products. Transformer?
+
+        :param metadata: Existing metadata without classification.
+        :param classification: Arbitrary object classifying this group.
+        :type metadata: BitCountingMetadata
+        :type classification: object
+        :returns: The metadata with added classification.
+        :rtype: ClassifiedBitCountingMetadata
+        """
+        return ClassifiedBitCountingMetadata(
+            max_value=metadata.max_value, size=metadata.size, avg=metadata.avg,
+            stdev=metadata.stdev, prev_avg=metadata.prev_avg,
+            classification=classification)
diff --git a/resources/tools/presentation/new/jumpavg/RunGroup.py b/resources/tools/presentation/new/jumpavg/RunGroup.py
new file mode 100644 (file)
index 0000000..808e02b
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class RunGroup(object):
+
+    def __init__(self, metadata, values):
+        """Create the group from metadata and values.
+
+        :param metadata: Metadata object to associate with the group.
+        :param values: The runs belonging to this group.
+        :type metadata: AbstractGroupMetadata
+        :type values: Iterable of float or od AvgStdevMetadata
+        """
+        self.metadata = metadata
+        self.values = values
diff --git a/resources/tools/presentation/new/jumpavg/__init__.py b/resources/tools/presentation/new/jumpavg/__init__.py
new file mode 100644 (file)
index 0000000..f9fc83a
--- /dev/null
@@ -0,0 +1,16 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+__init__ file for directory resources/tools/presentation/jumpavg
+"""
diff --git a/resources/tools/presentation/new/pal.py b/resources/tools/presentation/new/pal.py
new file mode 100644 (file)
index 0000000..013c921
--- /dev/null
@@ -0,0 +1,126 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""CSIT Presentation and analytics layer.
+"""
+
+import sys
+import argparse
+import logging
+
+from errors import PresentationError
+from environment import Environment, clean_environment
+from specification_parser import Specification
+from input_data_parser import InputData
+from generator_tables import generate_tables
+from generator_plots import generate_plots
+from generator_files import generate_files
+from static_content import prepare_static_content
+from generator_report import generate_report
+from generator_CPTA import generate_cpta
+
+
+def parse_args():
+    """Parse arguments from cmd line.
+
+    :returns: Parsed arguments.
+    :rtype: ArgumentParser
+    """
+
+    parser = argparse.ArgumentParser(description=__doc__,
+                                     formatter_class=argparse.
+                                     RawDescriptionHelpFormatter)
+    parser.add_argument("-s", "--specification",
+                        required=True,
+                        type=argparse.FileType('r'),
+                        help="Specification YAML file.")
+    parser.add_argument("-r", "--release",
+                        default="master",
+                        type=str,
+                        help="Release string of the product.")
+    parser.add_argument("-l", "--logging",
+                        choices=["DEBUG", "INFO", "WARNING",
+                                 "ERROR", "CRITICAL"],
+                        default="ERROR",
+                        help="Logging level.")
+    parser.add_argument("-f", "--force",
+                        action='store_true',
+                        help="Force removing the old build(s) if present.")
+
+    return parser.parse_args()
+
+
+def main():
+    """Main function."""
+
+    log_levels = {"NOTSET": logging.NOTSET,
+                  "DEBUG": logging.DEBUG,
+                  "INFO": logging.INFO,
+                  "WARNING": logging.WARNING,
+                  "ERROR": logging.ERROR,
+                  "CRITICAL": logging.CRITICAL}
+
+    args = parse_args()
+    logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s',
+                        datefmt='%Y/%m/%d %H:%M:%S',
+                        level=log_levels[args.logging])
+
+    logging.info("Application started.")
+    try:
+        spec = Specification(args.specification)
+        spec.read_specification()
+    except PresentationError:
+        logging.critical("Finished with error.")
+        return 1
+
+    if spec.output["output"] not in ("report", "CPTA"):
+        logging.critical("The output '{0}' is not supported.".
+                         format(spec.output["output"]))
+        return 1
+
+    ret_code = 1
+    try:
+        env = Environment(spec.environment, args.force)
+        env.set_environment()
+
+        prepare_static_content(spec)
+
+        data = InputData(spec)
+        data.download_and_parse_data(repeat=2)
+
+        generate_tables(spec, data)
+        generate_plots(spec, data)
+        generate_files(spec, data)
+
+        if spec.output["output"] == "report":
+            generate_report(args.release, spec)
+            logging.info("Successfully finished.")
+        elif spec.output["output"] == "CPTA":
+            sys.stdout.write(generate_cpta(spec, data))
+            logging.info("Successfully finished.")
+        ret_code = 0
+
+    except (KeyError, ValueError, PresentationError) as err:
+        logging.info("Finished with an error.")
+        logging.critical(str(err))
+    except Exception as err:
+        logging.info("Finished with an unexpected error.")
+        logging.critical(str(err))
+    finally:
+        if spec is not None:
+            clean_environment(spec.environment)
+        return ret_code
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/resources/tools/presentation/new/requirements.txt b/resources/tools/presentation/new/requirements.txt
new file mode 100644 (file)
index 0000000..a33848d
--- /dev/null
@@ -0,0 +1,11 @@
+Sphinx
+sphinx-rtd-theme
+robotframework==2.9.2
+sphinxcontrib-programoutput
+PyYAML
+pytz
+python-dateutil
+numpy
+pandas
+plotly
+PTable
diff --git a/resources/tools/presentation/new/run_cpta.sh b/resources/tools/presentation/new/run_cpta.sh
new file mode 100755 (executable)
index 0000000..6199703
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -x
+
+# set default values in config array
+typeset -A DIR
+
+DIR[WORKING]=_tmp
+
+# Install system dependencies
+sudo apt-get -y update
+sudo apt-get -y install libxml2 libxml2-dev libxslt-dev build-essential \
+    zlib1g-dev unzip
+
+# Create working directories
+mkdir ${DIR[WORKING]}
+
+# Create virtual environment
+virtualenv ${DIR[WORKING]}/env
+. ${DIR[WORKING]}/env/bin/activate
+
+# Install python dependencies:
+pip install -r requirements.txt
+
+export PYTHONPATH=`pwd`:`pwd`/jumpavg
+
+STATUS=$(python pal.py \
+    --specification specification_CPTA.yaml \
+    --logging INFO \
+    --force)
+RETURN_STATUS=$?
+
+echo ${STATUS}
+exit ${RETURN_STATUS}
diff --git a/resources/tools/presentation/new/run_report.sh b/resources/tools/presentation/new/run_report.sh
new file mode 100755 (executable)
index 0000000..d294640
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+set -x
+
+RELEASE=$1
+
+# set default values in config array
+typeset -A CFG
+typeset -A DIR
+
+DIR[WORKING]=_tmp
+CFG[BLD_LATEX]=1
+
+# Install system dependencies
+sudo apt-get -y update
+sudo apt-get -y install libxml2 libxml2-dev libxslt-dev build-essential \
+    zlib1g-dev unzip
+
+if [[ ${CFG[BLD_LATEX]} -eq 1 ]] ;
+then
+    sudo apt-get -y install xvfb texlive-latex-recommended \
+        texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra latexmk wkhtmltopdf inkscape
+    sudo sed -i.bak 's/^\(main_memory\s=\s\).*/\110000000/' /usr/share/texlive/texmf-dist/web2c/texmf.cnf
+fi
+
+# Create working directories
+mkdir ${DIR[WORKING]}
+
+# Create virtual environment
+virtualenv ${DIR[WORKING]}/env
+. ${DIR[WORKING]}/env/bin/activate
+
+# Install python dependencies:
+pip install -r requirements.txt
+
+export PYTHONPATH=`pwd`
+
+python pal.py \
+    --specification specification.yaml \
+    --release ${RELEASE} \
+    --logging INFO \
+    --force
+
+RETURN_STATUS=$(echo $?)
+exit ${RETURN_STATUS}
diff --git a/resources/tools/presentation/new/specification.yaml b/resources/tools/presentation/new/specification.yaml
new file mode 100644 (file)
index 0000000..97e616a
--- /dev/null
@@ -0,0 +1,3900 @@
+-
+  type: "environment"
+
+  paths:
+    # Top level directories:
+    ## Working directory
+    DIR[WORKING]: "_tmp"
+    ## Build directories
+    DIR[BUILD,HTML]: "_build"
+    DIR[BUILD,LATEX]: "_build_latex"
+
+    # Static .rst files
+    DIR[RST]: "../../../docs/report"
+
+    # Working directories
+    ## Input data files (.zip, .xml)
+    DIR[WORKING,DATA]: "{DIR[WORKING]}/data"
+    ## Static source files from git
+    DIR[WORKING,SRC]: "{DIR[WORKING]}/src"
+    DIR[WORKING,SRC,STATIC]: "{DIR[WORKING,SRC]}/_static"
+
+    # Static html content
+    DIR[STATIC]: "{DIR[BUILD,HTML]}/_static"
+    DIR[STATIC,VPP]: "{DIR[STATIC]}/vpp"
+    DIR[STATIC,DPDK]: "{DIR[STATIC]}/dpdk"
+    DIR[STATIC,ARCH]: "{DIR[STATIC]}/archive"
+
+    # Detailed test results
+    DIR[DTR]: "{DIR[WORKING,SRC]}/detailed_test_results"
+    DIR[DTR,PERF,DPDK]: "{DIR[DTR]}/dpdk_performance_results"
+    DIR[DTR,PERF,VPP]: "{DIR[DTR]}/vpp_performance_results"
+    DIR[DTR,MRR,VPP]: "{DIR[DTR]}/vpp_mrr_results"
+    DIR[DTR,PERF,COT]: "{DIR[DTR]}/cot_performance_results"
+    DIR[DTR,PERF,HC]: "{DIR[DTR]}/honeycomb_performance_results"
+    DIR[DTR,FUNC,VPP]: "{DIR[DTR]}/vpp_functional_results"
+    DIR[DTR,FUNC,VPP,CENTOS]: "{DIR[DTR]}/vpp_functional_results_centos"
+    DIR[DTR,FUNC,HC]: "{DIR[DTR]}/honeycomb_functional_results"
+    DIR[DTR,FUNC,NSHSFC]: "{DIR[DTR]}/nshsfc_functional_results"
+    DIR[DTR,PERF,VPP,IMPRV]: "{DIR[WORKING,SRC]}/vpp_performance_tests/performance_improvements"
+
+    # Detailed test configurations
+    DIR[DTC]: "{DIR[WORKING,SRC]}/test_configuration"
+    DIR[DTC,PERF,VPP]: "{DIR[DTC]}/vpp_performance_configuration"
+    DIR[DTC,MRR,VPP]: "{DIR[DTC]}/vpp_mrr_configuration"
+    DIR[DTC,FUNC,VPP]: "{DIR[DTC]}/vpp_functional_configuration"
+    DIR[DTC,FUNC,VPP,CENTOS]: "{DIR[DTC]}/vpp_functional_configuration_centos"
+
+    # Detailed tests operational data
+    DIR[DTO]: "{DIR[WORKING,SRC]}/test_operational_data"
+    DIR[DTO,PERF,VPP]: "{DIR[DTO]}/vpp_performance_operational_data"
+
+    # .css patch file to fix tables generated by Sphinx
+    DIR[CSS_PATCH_FILE]: "{DIR[STATIC]}/theme_overrides.css"
+    DIR[CSS_PATCH_FILE2]: "{DIR[WORKING,SRC,STATIC]}/theme_overrides.css"
+
+  urls:
+    URL[JENKINS,CSIT]: "https://jenkins.fd.io/view/csit/job"
+    URL[JENKINS,HC]: "https://jenkins.fd.io/view/hc2vpp/job"
+    URL[NEXUS]: "https://docs.fd.io/csit"
+    DIR[NEXUS]: "report/_static/archive"
+
+  make-dirs:
+  # List the directories which are created while preparing the environment.
+  # All directories MUST be defined in "paths" section.
+  - "DIR[WORKING,DATA]"
+  - "DIR[STATIC,VPP]"
+  - "DIR[STATIC,DPDK]"
+  - "DIR[STATIC,ARCH]"
+  - "DIR[BUILD,LATEX]"
+  - "DIR[WORKING,SRC]"
+  - "DIR[WORKING,SRC,STATIC]"
+
+  remove-dirs:
+  # List the directories which are deleted while cleaning the environment.
+  # All directories MUST be defined in "paths" section.
+  #- "DIR[BUILD,HTML]"
+  - "DIR[WORKING,DATA]"
+
+  build-dirs:
+  # List the directories where the results (build) is stored.
+  # All directories MUST be defined in "paths" section.
+  - "DIR[BUILD,HTML]"
+  - "DIR[BUILD,LATEX]"
+
+-
+  type: "configuration"
+  data-sets:
+    plot-vpp-http-server-performance:
+      csit-vpp-perf-1804-all:
+      - 39  # wrk
+      - 40  # wrk
+      - 41  # wrk
+      - 42  # wrk
+      - 43  # wrk
+      - 44  # wrk
+      - 45  # wrk
+      - 46  # wrk
+      - 47  # wrk
+      - 48  # wrk
+# TODO: Add the data sources
+#    vpp-meltdown-impact:
+#      csit-vpp-perf-1707-all:
+#      - 9
+#      - 10
+#      - 13
+#      csit-vpp-perf-1710-all:
+#      - 11l
+#      - 12
+#      - 13
+# TODO: Add the data sources
+#    vpp-spectre-impact:
+#      csit-vpp-perf-1707-all:
+#      - 9
+#      - 10
+#      - 13
+#      csit-vpp-perf-1710-all:
+#      - 11
+#      - 12
+#      - 13
+    vpp-performance-changes:
+      csit-vpp-perf-1710-all:
+      - 11
+      - 12
+      - 13
+      - 14
+      - 15
+      - 16
+      - 17
+      - 18
+      - 19
+      - 20
+      csit-vpp-perf-1801-all:
+      - 124  # sel
+      - 127  # sel
+      - 128  # sel
+      - 141  # sel
+      - 142  # sel
+      - 143  # sel
+      - 145  # sel
+      - 146  # sel
+      - 162  # sel
+      - 163  # sel
+      - 167  # sel
+      - 172  # sel acl only
+      csit-vpp-perf-1804-all:
+      - 21  # sel
+      - 22  # sel
+      - 23  # sel
+      - 24  # sel
+      - 27  # sel
+      - 28  # sel
+      - 29  # sel
+      - 30  # sel
+      - 31  # sel
+      - 35  # sel
+    vpp-performance-changes-mrr:
+      csit-vpp-perf-check-1801:
+      - 3
+      - 4
+      - 5
+      - 6
+      - 7
+      - 8
+      - 9
+      - 11
+      - 12
+      - 13
+      csit-vpp-perf-check-1804:
+      - 6   # mrr - sel
+      - 13  # mrr - sel
+      - 14  # mrr - sel
+      - 15  # mrr - sel
+      - 16  # mrr - sel
+      - 17  # mrr - sel
+      - 19  # mrr - sel
+      - 20  # mrr - sel
+      - 21  # mrr - sel
+      - 22  # mrr - sel
+    plot-throughput-speedup-analysis:
+      csit-vpp-perf-1804-all:
+      - 19  # full
+      - 20  # full
+      - 25  # full
+      - 49  # full
+      - 21  # sel
+      - 22  # sel
+      - 23  # sel
+      - 24  # sel
+      - 27  # sel
+      - 28  # sel
+      - 29  # sel
+      - 30  # sel
+      - 31  # sel
+      - 35  # sel
+    plot-ligato-throughput-speedup-analysis:
+      csit-ligato-perf-1804-all:
+      - 5   # sel
+      - 6   # sel
+      - 7   # sel
+      - 8   # sel
+      - 9   # sel
+      - 10  # sel
+      - 11  # sel
+      - 12  # sel
+      - 13  # sel
+      - 14  # sel
+#    performance-improvements:
+#      csit-vpp-perf-1707-all:
+#      - 9
+#      - 10
+#      - 13
+#      - 14
+#      - 15
+#      - 16
+#      - 17
+#      - 18
+#      - 19
+#      - 21
+#      csit-vpp-perf-1710-all:
+#      - 11
+#      - 12
+#      - 13
+#      - 14
+#      - 15
+#      - 16
+#      - 17
+#      - 18
+#      - 19
+#      - 20
+#      csit-vpp-perf-1801-all:
+#      - 124
+#      - 127
+#      - 128
+#      csit-ligato-perf-1710-all:
+#      - 5
+#      - 7
+#      - 8
+#      - 9
+#      - 10
+#      - 11
+#      - 12
+#      - 13
+#      - 16
+#      - 17
+#      csit-ligato-perf-1801-all:
+#      - 16  # sel
+#      - 17  # sel
+#      - 18  # sel
+#      - 19  # sel
+#      - 20  # sel
+#      - 21  # sel
+#      - 22  # sel
+#      - 23  # sel
+#      - 24  # sel
+    vpp-perf-results:
+      csit-vpp-perf-1804-all:
+      - 19  # full
+      - 20  # full
+      - 25  # full
+      - 49  # full
+    vpp-func-results:
+      csit-vpp-functional-1804-ubuntu1604-virl:
+      - 229
+    vpp-func-results-centos:
+      csit-vpp-functional-1804-centos7-virl:
+      - 238
+    vpp-mrr-results:
+      csit-vpp-perf-check-1804:
+      - 5   # mrr - full
+    ligato-perf-results:
+      csit-ligato-perf-1804-all:
+      - 4  # full
+    dpdk-perf-results:
+      csit-dpdk-perf-1804-all:
+      - 13
+    hc-func-results:
+      csit-hc2vpp-verify-func-1804-ubuntu1604:
+      - 3
+    nsh-func-results:
+      csit-nsh_sfc-verify-func-1804-ubuntu1604-virl:
+      - 7
+    plot-vpp-throughput-latency:
+      csit-vpp-perf-1804-all:
+      - 19  # full
+      - 20  # full
+      - 25  # full
+      - 49  # full
+      - 21  # sel
+      - 22  # sel
+      - 23  # sel
+      - 24  # sel
+      - 27  # sel
+      - 28  # sel
+      - 29  # sel
+      - 30  # sel
+      - 31  # sel
+      - 35  # sel
+    plot-dpdk-throughput-latency:
+      csit-dpdk-perf-1804-all:
+      - 4
+      - 5
+      - 6
+      - 7
+      - 8
+      - 9
+      - 10
+      - 11
+      - 12
+      - 13
+    plot-ligato-throughput-latency:
+      csit-ligato-perf-1804-all:
+      - 5   # sel
+      - 6   # sel
+      - 7   # sel
+      - 8   # sel
+      - 9   # sel
+      - 10  # sel
+      - 11  # sel
+      - 12  # sel
+      - 13  # sel
+      - 14  # sel
+
+  plot-layouts:
+
+    plot-cps:
+      xaxis:
+        autorange: True
+        autotick: False
+        fixedrange: False
+        gridcolor: "rgb(238, 238, 238)"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        tickmode: "linear"
+        title: "Indexed Test Cases"
+        zeroline: False
+      yaxis:
+        gridcolor: "rgb(238, 238, 238)'"
+        hoverformat: ".4s"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        range: []
+        rangemode: "tozero"
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        title: "Connections Per Second [cps]"
+        zeroline: False
+      boxmode: "group"
+      boxgroupgap: 0.5
+      autosize: False
+      margin:
+        t: 50
+        b: 20
+        l: 50
+        r: 20
+      showlegend: True
+      legend:
+        orientation: "h"
+      width: 700
+      height: 1000
+
+    plot-rps:
+      xaxis:
+        autorange: True
+        autotick: False
+        fixedrange: False
+        gridcolor: "rgb(238, 238, 238)"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        tickmode: "linear"
+        title: "Indexed Test Cases"
+        zeroline: False
+      yaxis:
+        gridcolor: "rgb(238, 238, 238)'"
+        hoverformat: ".4s"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        range: []
+        rangemode: "tozero"
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        title: "Requests Per Second [rps]"
+        zeroline: False
+      boxmode: "group"
+      boxgroupgap: 0.5
+      autosize: False
+      margin:
+        t: 50
+        b: 20
+        l: 50
+        r: 20
+      showlegend: True
+      legend:
+        orientation: "h"
+      width: 700
+      height: 1000
+
+    plot-throughput:
+      xaxis:
+        autorange: True
+        autotick: False
+        fixedrange: False
+        gridcolor: "rgb(238, 238, 238)"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        tickmode: "linear"
+        title: "Indexed Test Cases"
+        zeroline: False
+      yaxis:
+        gridcolor: "rgb(238, 238, 238)'"
+        hoverformat: ".4s"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        range: []
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        title: "Packets Per Second [pps]"
+        zeroline: False
+      boxmode: "group"
+      boxgroupgap: 0.5
+      autosize: False
+      margin:
+        t: 50
+        b: 20
+        l: 50
+        r: 20
+      showlegend: True
+      legend:
+        orientation: "h"
+      width: 700
+      height: 1000
+
+    plot-latency:
+      xaxis:
+        autorange: True
+        autotick: False
+        fixedrange: False
+        gridcolor: "rgb(238, 238, 238)"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        tickmode: "linear"
+        title: "Indexed Test Cases"
+        zeroline: False
+      yaxis:
+        gridcolor: "rgb(238, 238, 238)'"
+        hoverformat: ""
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        range: []
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        title: "Latency min/avg/max [uSec]"
+        zeroline: False
+      boxmode: "group"
+      boxgroupgap: 0.5
+      autosize: False
+      margin:
+        t: 50
+        b: 20
+        l: 50
+        r: 20
+      showlegend: True
+      legend:
+        orientation: "h"
+      width: 700
+      height: 1000
+
+    plot-throughput-speedup-analysis:
+      xaxis:
+        autorange: True
+        autotick: False
+        fixedrange: False
+        gridcolor: "rgb(238, 238, 238)"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        tickmode: "linear"
+        tickangle: 270
+        zeroline: False
+      yaxis:
+        title: "Throughput speedup factor"
+        gridcolor: "rgb(238, 238, 238)"
+        hoverformat: ".4s"
+        linecolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        range: []
+        showgrid: True
+        showline: True
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        zeroline: False
+      legend:
+        orientation: "h"
+        xanchor: "center"
+        yanchor: "top"
+        x: 0.5
+        y: 1
+        bgcolor: "rgba(255, 255, 255, 0)"
+        bordercolor: "rgba(255, 255, 255, 0)"
+      barmode: "group"
+      bargap: 0.15
+      bargroupgap: 0.1
+      autosize: False
+      margin:
+          't': 50
+          'b': 300
+          'l': 50
+          'r': 20
+      showlegend: True
+      width: 700
+      height: 1000
+
+-
+  type: "static"
+  src-path: "{DIR[RST]}"
+  dst-path: "{DIR[WORKING,SRC]}"
+
+-
+  type: "input"  # Ignored in debug mode
+  general:
+    file-name: "robot-plugin.zip"
+    file-format: ".zip"
+    download-path: "{job}/{build}/robot/report/*zip*/{filename}"
+    extract: "robot-plugin/output.xml"
+  builds:
+#    csit-vpp-perf-1707-all:
+#    - 9
+#    - 10
+#    - 13
+#    - 14
+#    - 15
+#    - 16
+#    - 17
+#    - 18
+#    - 19
+#    - 21
+    csit-vpp-perf-1710-all:
+    - 11
+    - 12
+    - 13
+    - 14
+    - 15
+    - 16
+    - 17
+    - 18
+    - 19
+    - 20
+    csit-vpp-perf-1801-all:
+#    - 122  # full
+#    - 126  # full
+#    - 129  # full
+#    - 140  # full
+    - 124  # sel
+    - 127  # sel
+    - 128  # sel
+    - 141  # sel
+    - 142  # sel
+    - 143  # sel
+    - 145  # sel
+    - 146  # sel
+    - 162  # sel
+    - 163  # sel
+    - 167  # sel
+    - 172  # sel acl only
+    csit-vpp-perf-1804-all:
+    - 19  # full
+    - 20  # full
+    - 25  # full
+    - 49  # full
+    - 21  # sel
+    - 22  # sel
+    - 23  # sel
+    - 24  # sel
+    - 27  # sel
+    - 28  # sel
+    - 29  # sel
+    - 30  # sel
+    - 31  # sel
+    - 35  # sel
+    - 39  # wrk
+    - 40  # wrk
+    - 41  # wrk
+    - 42  # wrk
+    - 43  # wrk
+    - 44  # wrk
+    - 45  # wrk
+    - 46  # wrk
+    - 47  # wrk
+    - 48  # wrk
+    csit-vpp-perf-check-1801:
+    - 3   # mrr
+    - 4   # mrr
+    - 5   # mrr
+    - 6   # mrr
+    - 7   # mrr
+    - 8   # mrr
+    - 9   # mrr
+    - 11  # mrr
+    - 12  # mrr
+    - 13  # mrr
+    csit-vpp-perf-check-1804:
+    - 5   # mrr - full
+    - 6   # mrr - sel
+    - 13  # mrr - sel
+    - 14  # mrr - sel
+    - 15  # mrr - sel
+    - 16  # mrr - sel
+    - 17  # mrr - sel
+    - 19  # mrr - sel
+    - 20  # mrr - sel
+    - 21  # mrr - sel
+    - 22  # mrr - sel
+#    csit-ligato-perf-1710-all:
+#    - 5
+#    - 7
+#    - 8
+#    - 9
+#    - 10
+#    - 11
+#    - 12
+#    - 13
+#    - 16
+#    - 17
+#    csit-ligato-perf-1801-all:
+#    - 16  # sel
+#    - 17  # sel
+#    - 18  # sel
+#    - 19  # sel
+#    - 20  # sel
+#    - 21  # sel
+#    - 22  # sel
+#    - 23  # sel
+#    - 24  # sel
+#    - 25  # full
+    csit-ligato-perf-1804-all:
+    - 4   # full
+    - 5   # sel
+    - 6   # sel
+    - 7   # sel
+    - 8   # sel
+    - 9   # sel
+    - 10  # sel
+    - 11  # sel
+    - 12  # sel
+    - 13  # sel
+    - 14  # sel
+    csit-dpdk-perf-1804-all:
+    - 4
+    - 5
+    - 6
+    - 7
+    - 8
+    - 9
+    - 10
+    - 11
+    - 12
+    - 13
+    csit-vpp-functional-1804-ubuntu1604-virl:
+    - 229
+    csit-vpp-functional-1804-centos7-virl:
+    - 238
+    csit-nsh_sfc-verify-func-1804-ubuntu1604-virl:
+    - 7
+    csit-hc2vpp-verify-func-1804-ubuntu1604:
+    - 3
+
+-
+  type: "output"
+  output: "report"
+  format:
+    html:
+    - full
+    pdf:
+    - minimal
+
+################################################################################
+###                               T A B L E S                                ###
+################################################################################
+
+#-
+#  type: "table"
+#  title: "Performance Impact of Meltdown Patches"
+#  algorithm: "table_performance_comparison"
+#  output-file-ext: ".csv"
+## TODO: specify dir
+#  output-file: "{DIR[STATIC,VPP]}/meltdown-impact"
+#  reference:
+#    title: "No Meltdown"
+## TODO: specify data sources
+#    data:
+#      csit-vpp-perf-1707-all:
+#      - 9
+#      - 10
+#      - 13
+#  compare:
+#    title: "Meltdown Patches Applied"
+## TODO: specify data sources
+#    data:
+#      csit-vpp-perf-1710-all:
+#      - 11
+#      - 12
+#      - 13
+#  data:
+#    "vpp-meltdown-impact"
+#  filter: "all"
+#  parameters:
+#  - "name"
+#  - "parent"
+#  - "throughput"
+#  # Number of the best and the worst tests presented in the table. Use 0 (zero)
+#  # to present all tests.
+#  nr-of-tests-shown: 20
+#
+#-
+#  type: "table"
+#  title: "Performance Impact of Spectre Patches"
+#  algorithm: "table_performance_comparison"
+#  output-file-ext: ".csv"
+## TODO: specify dir
+#  output-file: "{DIR[STATIC,VPP]}/meltdown-spectre-impact"
+#  reference:
+#    title: "No Spectre"
+## TODO: specify data sources
+#    data:
+#      csit-vpp-perf-1707-all:
+#      - 9
+#      - 10
+#      - 13
+#  compare:
+#    title: "Spectre Patches Applied"
+## TODO: specify data sources
+#    data:
+#      csit-vpp-perf-1710-all:
+#      - 11
+#      - 12
+#      - 13
+#  data:
+#    "vpp-spectre-impact"
+#  filter: "all"
+#  parameters:
+#  - "name"
+#  - "parent"
+#  - "throughput"
+#  # Number of the best and the worst tests presented in the table. Use 0 (zero)
+#  # to present all tests.
+#  nr-of-tests-shown: 20
+
+-
+  type: "table"
+  title: "VPP Performance Changes"
+  algorithm: "table_performance_comparison"
+  output-file-ext: ".csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-changes"
+  history:
+    -
+      title: "rls1710"
+      data:
+        csit-vpp-perf-1710-all:
+        - 11
+        - 12
+        - 13
+        - 14
+        - 15
+        - 16
+        - 17
+        - 18
+        - 19
+        - 20
+  reference:
+    title: "rls1801"
+    data:
+      csit-vpp-perf-1801-all:
+      - 124  # sel
+      - 127  # sel
+      - 128  # sel
+      - 141  # sel
+      - 142  # sel
+      - 143  # sel
+      - 145  # sel
+      - 146  # sel
+      - 162  # sel
+      - 163  # sel
+      - 167  # sel
+      - 172  # sel acl only
+  compare:
+    title: "rls1804"
+    data:
+      csit-vpp-perf-1804-all:
+      - 21  # sel
+      - 22  # sel
+      - 23  # sel
+      - 24  # sel
+      - 27  # sel
+      - 28  # sel
+      - 29  # sel
+      - 30  # sel
+      - 31  # sel
+      - 35  # sel
+  data: "vpp-performance-changes"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "throughput"
+  # Number of the best and the worst tests presented in the table. Use 0 (zero)
+  # to present all tests.
+  nr-of-tests-shown: 20
+  outlier-const: 1.5
+
+-
+  type: "table"
+  title: "VPP Performance Changes - MRR"
+  algorithm: "table_performance_comparison_mrr"
+  output-file-ext: ".csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-changes-mrr"
+  reference:
+    title: "Release 1801"
+    data:
+      csit-vpp-perf-check-1801:
+      - 3
+      - 4
+      - 5
+      - 6
+      - 7
+      - 8
+      - 9
+      - 11
+      - 12
+      - 13
+  compare:
+    title: "Release 1804"
+    data:
+      csit-vpp-perf-check-1804:
+      - 6   # mrr - sel
+      - 13  # mrr - sel
+      - 14  # mrr - sel
+      - 15  # mrr - sel
+      - 16  # mrr - sel
+      - 17  # mrr - sel
+      - 19  # mrr - sel
+      - 20  # mrr - sel
+      - 21  # mrr - sel
+      - 22  # mrr - sel
+  data: "vpp-performance-changes-mrr"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "result"
+  # Number of the best and the worst tests presented in the table. Use 0 (zero)
+  # to present all tests.
+  nr-of-tests-shown: 20
+  outlier-const: 1.5
+
+#-
+#  type: "table"
+#  title: "Performance improvements"
+#  algorithm: "table_performance_improvements"
+#  template: "{DIR[DTR,PERF,VPP,IMPRV]}/tmpl_performance_improvements.csv"
+#  output-file-ext: ".csv"
+#  output-file: "{DIR[DTR,PERF,VPP,IMPRV]}/performance_improvements"
+#  columns:
+#  -
+#    title: "Test Name"
+#    data: "template 1"
+#  -
+#    title: "16.09 mean [Mpps]"
+#    data: "template 2"
+#  -
+#    title: "17.01 mean [Mpps]"
+#    data: "template 3"
+#  -
+#    title: "17.04 mean [Mpps]"
+#    data: "template 4"
+#  -
+#    title: "17.07 mean [Mpps]"
+#    data: "data csit-vpp-perf-1707-all mean"
+#  -
+#    title: "17.10 mean [Mpps]"
+#    data: "data csit-vpp-perf-1710-all csit-ligato-perf-1710-all mean"
+#  -
+#    title: "18.01 mean [Mpps]"
+#    data: "data csit-vpp-perf-1801-all csit-ligato-perf-1801-all mean"
+#  -
+#    title: "18.01 stdev [Mpps]"
+#    data: "data csit-vpp-perf-1801-all csit-ligato-perf-1801-all stdev"
+#  -
+#    title: "17.10 to 18.01 change [%]"
+#    data: "operation relative_change 5 6"
+#  rows: "generated"
+#  data:
+#    "performance-improvements"
+#  filter: "template"
+#  parameters:
+#  - "throughput"
+
+-
+  type: "table"
+  title: "Detailed Test Results - VPP Performance Results"
+  algorithm: "table_merged_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,PERF,VPP]}/vpp_performance_results"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data msg"
+  rows: "generated"
+  data:
+    "vpp-perf-results"
+  filter: "not 'NDRCHK' and not 'PDRCHK'"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "msg"
+
+-
+  type: "table"
+  title: "Test configuration - VPP Performance Test Configs"
+  algorithm: "table_merged_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTC,PERF,VPP]}/vpp_test_configuration"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "VPP API Test (VAT) Commands History - Commands Used Per Test Case"
+    data: "data vat-history"
+  rows: "generated"
+  data:
+    "vpp-perf-results"
+  filter: "not 'NDRCHK' and not 'PDRCHK'"
+  parameters:
+  - "parent"
+  - "name"
+  - "vat-history"
+
+-
+  type: "table"
+  title: "Test Operational Data - VPP Performance Operational Data"
+  algorithm: "table_merged_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTO,PERF,VPP]}/vpp_test_operational"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "VPP Operational Data - Outputs of 'show runtime' at NDR packet rate"
+    data: "data show-run"
+  rows: "generated"
+  data:
+    "vpp-perf-results"
+  filter: "not 'NDRCHK' and not 'PDRCHK'"
+  parameters:
+  - "parent"
+  - "name"
+  - "show-run"
+
+-
+  type: "table"
+  title: "Detailed Test Results - VPP MRR Results"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,MRR,VPP]}/vpp_mrr_results"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data msg"
+  rows: "generated"
+  data:
+    "vpp-mrr-results"
+  filter: "'MRR'"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "msg"
+
+-
+  type: "table"
+  title: "Test configuration - VPP MRR Test Configs"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTC,MRR,VPP]}/vpp_mrr_test_configuration"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "VPP API Test (VAT) Commands History - Commands Used Per Test Case"
+    data: "data vat-history"
+  rows: "generated"
+  data:
+    "vpp-mrr-results"
+  filter: "'MRR'"
+  parameters:
+  - "parent"
+  - "name"
+  - "vat-history"
+
+-
+  type: "table"
+  title: "Detailed Test Results - VPP Functional Results"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,FUNC,VPP]}/vpp_functional_results"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data status"
+  rows: "generated"
+  data:
+    "vpp-func-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "status"
+
+-
+  type: "table"
+  title: "Detailed Test Results - VPP Functional Results - CentOS"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,FUNC,VPP,CENTOS]}/vpp_functional_results_centos"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data status"
+  rows: "generated"
+  data:
+    "vpp-func-results-centos"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "status"
+
+-
+  type: "table"
+  title: "Test configuration - VPP Functional Test Configs"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTC,FUNC,VPP]}/vpp_functional_configuration"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "VPP API Test (VAT) Commands History - Commands Used Per Test Case"
+    data: "data vat-history"
+  rows: "generated"
+  data:
+    "vpp-func-results"
+  filter: "all"
+  parameters:
+  - "parent"
+  - "name"
+  - "vat-history"
+
+-
+  type: "table"
+  title: "Test configuration - VPP Functional Test Configs - CentOS"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTC,FUNC,VPP,CENTOS]}/vpp_functional_configuration_centos"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "VPP API Test (VAT) Commands History - Commands Used Per Test Case"
+    data: "data vat-history"
+  rows: "generated"
+  data:
+    "vpp-func-results-centos"
+  filter: "all"
+  parameters:
+  - "parent"
+  - "name"
+  - "vat-history"
+
+-
+  type: "table"
+  title: "Detailed Test Results - Container Orchestrated Topologies Performance Results"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,PERF,COT]}/cot_performance_results"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data msg"
+  rows: "generated"
+  data:
+    "ligato-perf-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "msg"
+
+-
+  type: "table"
+  title: "Detailed Test Results - DPDK Performance Results"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,PERF,DPDK]}/dpdk_performance_results"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data msg"
+  rows: "generated"
+  data:
+    "dpdk-perf-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "msg"
+
+-
+  type: "table"
+  title: "Detailed Test Results - Honeycomb Functional Results"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,FUNC,HC]}/hc_functional_results"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data status"
+  rows: "generated"
+  data:
+    "hc-func-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "status"
+
+-
+  type: "table"
+  title: "Detailed Test Results - NSH SFC Functional Results"
+  algorithm: "table_details"
+  output-file-ext: ".csv"
+  output-file: "{DIR[DTR,FUNC,NSHSFC]}/nsh_sfc_functional_results"
+  columns:
+  -
+    title: "Name"
+    data: "data name"
+  -
+    title: "Documentation"
+    data: "data doc"
+  -
+    title: "Status"
+    data: "data status"
+  rows: "generated"
+  data:
+    "nsh-func-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "parent"
+  - "doc"
+  - "status"
+
+################################################################################
+###                                F I L E S                                 ###
+################################################################################
+
+-
+  type: "file"
+  title: "VPP Performance Results"
+  algorithm: "file_merged_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,PERF,VPP]}/vpp_performance_results"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,PERF,VPP]}"
+  data:
+    "vpp-perf-results"
+  filter: "not 'NDRCHK' and not 'PDRCHK'"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  - "parent"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP Performance Configuration"
+  algorithm: "file_merged_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTC,PERF,VPP]}/vpp_performance_configuration"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTC,PERF,VPP]}"
+  data:
+    "vpp-perf-results"
+  filter: "not 'NDRCHK' and not 'PDRCHK'"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  - "parent"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP Performance Operational Data"
+  algorithm: "file_merged_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTO,PERF,VPP]}/vpp_performance_operational_data"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTO,PERF,VPP]}"
+  data:
+    "vpp-perf-results"
+  filter: "not 'NDRCHK' and not 'PDRCHK'"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  - "parent"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP MRR Results"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,MRR,VPP]}/vpp_mrr_results"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,MRR,VPP]}"
+  data:
+    "vpp-mrr-results"
+  filter: "'MRR'"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  - "parent"
+  data-start-level: 2  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP MRR Configuration"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTC,MRR,VPP]}/vpp_mrr_configuration"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTC,MRR,VPP]}"
+  data:
+    "vpp-mrr-results"
+  filter: "'MRR'"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  - "parent"
+  data-start-level: 2  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP Functional Results"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,FUNC,VPP]}/vpp_functional_results"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,FUNC,VPP]}"
+  data:
+    "vpp-func-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP Functional Results - CentOS"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,FUNC,VPP,CENTOS]}/vpp_functional_results_centos"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,FUNC,VPP,CENTOS]}"
+  data:
+    "vpp-func-results-centos"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP Functional Configuration"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTC,FUNC,VPP]}/vpp_functional_configuration"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTC,FUNC,VPP]}"
+  data:
+    "vpp-func-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "VPP Functional Configuration - CentOS"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTC,FUNC,VPP,CENTOS]}/vpp_functional_configuration_centos"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTC,FUNC,VPP,CENTOS]}"
+  data:
+    "vpp-func-results-centos"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "Container Orchestrated Performance Results"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,PERF,COT]}/cot_performance_results"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,PERF,COT]}"
+  data:
+    "ligato-perf-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  data-start-level: 2  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "DPDK Performance Results"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,PERF,DPDK]}/dpdk_performance_results"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,PERF,DPDK]}"
+  data:
+    "dpdk-perf-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  chapters:
+  - "suites"
+  data-start-level: 2  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "Honeycomb Functional Results"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,FUNC,HC]}/honeycomb_functional_results"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,FUNC,HC]}"
+  data:
+    "hc-func-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  chapters:
+  - "suites"
+  data-start-level: 3  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+-
+  type: "file"
+  title: "NSH SFC Functional Results"
+  algorithm: "file_test_results"
+  output-file-ext: ".rst"
+  output-file: "{DIR[DTR,FUNC,NSHSFC]}/nshsfc_functional_results"
+  file-header: "\n.. |br| raw:: html\n\n    <br />\n\n\n.. |prein| raw:: html\n\n    <pre>\n\n\n.. |preout| raw:: html\n\n    </pre>\n\n"
+  dir-tables: "{DIR[DTR,FUNC,NSHSFC]}"
+  data:
+    "nsh-func-results"
+  filter: "all"
+  parameters:
+  - "name"
+  - "doc"
+  - "level"
+  chapters:
+  - "suites"
+  data-start-level: 2  # 0, 1, 2, ...
+  chapters-start-level: 2  # 0, 1, 2, ...
+
+################################################################################
+###                                P L O T S                                 ###
+################################################################################
+
+# Plots VPP HTTP Server Performance
+-
+  type: "plot"
+  title: "VPP HTTP Server Performance"
+  algorithm: "plot_http_server_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/http-server-performance-cps"
+  data:
+    "plot-vpp-http-server-performance"
+  # Keep this formatting, the filter is enclosed with " (quotation mark) and
+  # each tag is enclosed with ' (apostrophe).
+  filter: "'HTTP' and 'TCP_CPS'"
+  parameters:
+  - "result"
+  - "name"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "VPP HTTP Server Performance"
+    layout:
+      "plot-cps"
+
+-
+  type: "plot"
+  title: "VPP HTTP Server Performance"
+  algorithm: "plot_http_server_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/http-server-performance-rps"
+  data:
+    "plot-vpp-http-server-performance"
+  filter: "'HTTP' and 'TCP_RPS'"
+  parameters:
+  - "result"
+  - "name"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "VPP HTTP Server Performance"
+    layout:
+      "plot-rps"
+
+# Plot Throughput Speedup Analysis
+
+# L2 - 10ge2p1x520 - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-l2-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'BASE' and 'NDRDISC' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'LXC' and not 'DOCKER'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# L2 - 40ge2p1xl710 - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-l2-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and 'BASE' and 'NDRDISC' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'LXC' and not 'DOCKER'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# L2 - 10ge2p1x520 - PDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-l2-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'LXC' and not 'DOCKER'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IPv4 - 10ge2p1x520 - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-ethip4-ip4(base|scale)*ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-ip4-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'IP4FWD' and ('BASE' or 'SCALE') and 'NDRDISC' and not 'VHOST' and not 'FEATURE' and not 'DOT1Q' and not 'IPSEC'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-ethip4-ip4(base|scale)*ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IPv4 - 40ge2p1xl710 - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-ethip4-ip4(base|scale)*ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-ip4-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and 'IP4FWD' and ('BASE' or 'SCALE') and 'NDRDISC' and not 'VHOST' and not 'FEATURE' and not 'DOT1Q' and not 'IPSEC'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-ethip4-ip4(base|scale)*ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IPv4 - 10ge2p1x520 - PDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-ethip4-ip4(base|scale)*pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-ip4-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'IP4FWD' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and not 'VHOST' and not 'FEATURE' and not 'DOT1Q' and not 'IPSEC'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-ethip4-ip4(base|scale)*pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IPv6 - 10ge2p1x520 - NDR
+-
+  type: "plot"
+  title: "TSA: 78B-*-ethip6-ip6(base|scale)*ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-78B-ip6-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '78B' and 'IP6FWD' and ('BASE' or 'SCALE') and 'NDRDISC' and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "78B-*-ethip6-ip6(base|scale)*ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IPv6 - 40ge2p1xl710 - NDR
+-
+  type: "plot"
+  title: "TSA: 78B-*-ethip6-ip6(base|scale)*ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-78B-ip6-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '78B' and 'IP6FWD' and ('BASE' or 'SCALE') and 'NDRDISC' and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "78B-*-ethip6-ip6(base|scale)*ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IPv6 - 10ge2p1x520 - PDR
+-
+  type: "plot"
+  title: "TSA: 78B-*-ethip6-ip6(base|scale)*pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-78B-ip6-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '78B' and 'IP6FWD' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "78B-*-ethip6-ip6(base|scale)*pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# SRv6 - 10ge2p1x520 - NDR
+-
+  type: "plot"
+  title: "TSA: 78B-*-ethip6-ip6(base|scale)*ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-78B-srv6-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '78B' and 'FEATURE' and 'NDRDISC' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "78B-*-ethip6-ip6(base|scale)*ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# SRv6 - 10ge2p1x520 - PDR
+-
+  type: "plot"
+  title: "TSA: 78B-*-ethip6-ip6(base|scale)*pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-78B-srv6-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '78B' and 'FEATURE' and 'PDRDISC' and not 'NDRDISC' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "78B-*-ethip6-ip6(base|scale)*pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IP4_overlay - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-ethip4-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'ENCAP' and 'NDRDISC' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IP4_overlay - PDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-ethip4[a-z0-9]+-[a-z0-9]*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-ethip4-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'ENCAP' and 'PDRDISC' and not 'NDRDISC' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-ethip4[a-z0-9]+-[a-z0-9]*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IP6_overlay - NDR
+-
+  type: "plot"
+  title: "TSA: 78B-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-78B-ethip6-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '78B' and 'ENCAP' and 'NDRDISC' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "78B-*-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# IP6_overlay - PDR
+-
+  type: "plot"
+  title: "TSA: 78B-*-ethip6[a-z0-9]+-[a-z0-9]*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-78B-ethip6-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '78B' and 'ENCAP' and 'PDRDISC' and not 'NDRDISC' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "78B-*-ethip6[a-z0-9]+-[a-z0-9]*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# VM VHOST - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-vhost-sel1-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'NDRDISC' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-vhost-sel1-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and 'NDRDISC' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-vhost-sel2-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'NDRDISC' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x710-64B-vhost-sel2-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X710' and '64B' and 'NDRDISC' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-vhost-sel2-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and 'NDRDISC' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# VM VHOST - PDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-vhost-sel1-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'PDRDISC' and not 'NDRDISC' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-vhost-sel1-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and 'PDRDISC' and not 'NDRDISC' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-vhost-sel2-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'PDRDISC' and not 'NDRDISC' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x710-64B-vhost-sel2-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X710' and '64B' and 'PDRDISC' and not 'NDRDISC' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*vhost.*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-vhost-sel2-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and 'PDRDISC' and not 'NDRDISC' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# CRYPTO - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*ipsec.*-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-ipsechw-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and not 'VHOST' and 'IP4FWD' and 'NDRDISC' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*ipsec.*-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# CRYPTO - PDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-.*ipsec.*-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/40ge2p1xl710-64B-ipsechw-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-XL710' and '64B' and not 'VHOST' and 'IP4FWD' and 'PDRDISC' and not 'NDRDISC' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-.*ipsec.*-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# Container memif - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-container-memif-tsa-ndrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'BASE' and 'NDRDISC' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# Container memif - PDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-container-memif-tsa-pdrdisc"
+  data:
+    "plot-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# Container orchestrated - NDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-container-orchestrated-tsa-ndrdisc"
+  data:
+    "plot-ligato-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and ('BASE' or 'SCALE') and 'NDRDISC' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x710-64B-container-orchestrated-tsa-ndrdisc"
+  data:
+    "plot-ligato-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X710' and '64B' and ('BASE' or 'SCALE') and 'NDRDISC' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# Container orchestrated - PDR
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x520-64B-container-orchestrated-tsa-pdrdisc"
+  data:
+    "plot-ligato-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X520-DA2' and '64B' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+-
+  type: "plot"
+  title: "TSA: 64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+  algorithm: "plot_throughput_speedup_analysis"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/10ge2p1x710-64B-container-orchestrated-tsa-pdrdisc"
+  data:
+    "plot-ligato-throughput-speedup-analysis"
+  filter: "'NIC_Intel-X710' and '64B' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  - "tags"
+  layout:
+    title: "64B-*-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+    layout:
+      "plot-throughput-speedup-analysis"
+
+# Plot packets per second
+
+# VPP L2 sel1
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel1-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  # Keep this formatting, the filter is enclosed with " (quotation mark) and
+  # each tag is enclosed with ' (apostrophe).
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-l2-sel1-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel1-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-l2-sel1-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP L2 sel2
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel2-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'FEATURE' and ('ACL10' or 'ACL50') and '10k_FLOWS' and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-l2-sel2-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'FEATURE' and ('ACL10' or 'ACL50') and '10k_FLOWS' and 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel2-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'FEATURE' and ('ACL10' or 'ACL50') and '10k_FLOWS' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-l2-sel2-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'FEATURE' and ('ACL10' or 'ACL50') and '10k_FLOWS' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP IP4
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ethip4-ip4-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '1T1C' and 'IP4FWD' and not 'ACL1' and not 'ACL10' and not '100_FLOWS' and not '100k_FLOWS' and not 'IPSEC' and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ethip4-ip4-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '2T2C' and 'IP4FWD' and not 'ACL1' and not 'ACL10' and not '100_FLOWS' and not '100k_FLOWS' and not 'IPSEC' and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-ethip4-ip4[a-z0-9]+-[a-z-]*pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ethip4-ip4-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE' or 'FEATURE') and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'IP4FWD' and not 'ACL1' and not 'ACL10' and not '100_FLOWS' and not '100k_FLOWS' and not 'IPSEC' and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-ethip4-ip4[a-z0-9]+-[a-z-]*pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-ethip4-ip4[a-z0-9]+-[a-z-]*pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ethip4-ip4-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE' or 'FEATURE') and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'IP4FWD' and not 'ACL1' and not 'ACL10' and not '100_FLOWS' and not '100k_FLOWS' and not 'IPSEC' and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-ethip4-ip4[a-z0-9]+-[a-z-]*pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP IP6
+-
+  type: "plot"
+  title: "VPP Performance 78B-1t1c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-ip6-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '1T1C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-1t1c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-2t2c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-ip6-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '2T2C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-2t2c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-1t1c-ethip6-ip6[a-z0-9]+-[a-z-]*pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-ip6-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and ('BASE' or 'SCALE' or 'FEATURE') and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-1t1c-ethip6-ip6[a-z0-9]+-[a-z-]*pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-2t2c-ethip6-ip6[a-z0-9]+-[a-z-]*pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-ip6-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and ('BASE' or 'SCALE' or 'FEATURE') and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-2t2c-ethip6-ip6[a-z0-9]+-[a-z-]*pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP SRv6
+-
+  type: "plot"
+  title: "VPP Performance 78B-1t1c-ethip6*srv6*ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-srv6-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'FEATURE' and 'NDRDISC' and '1T1C' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-1t1c-ethip6*srv6*ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-2t2c-ethip6*srv6*ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-srv6-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'FEATURE' and 'NDRDISC' and '2T2C' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-2t2c-ethip6*srv6*ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-1t1c-ethip6*srv6*pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-srv6-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'FEATURE' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-1t1c-ethip6*srv6*pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-2t2c-ethip6*srv6*pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-srv6-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'FEATURE' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-2t2c-ethip6*srv6*pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP IP4_overlay
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ethip4-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'ENCAP' and 'NDRDISC' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ethip4-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'ENCAP' and 'NDRDISC' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-ethip4[a-z0-9]+-[a-z0-9]*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ethip4-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'ENCAP' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-ethip4[a-z0-9]+-[a-z0-9]*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-ethip4[a-z0-9]+-[a-z0-9]*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ethip4-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'ENCAP' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-ethip4[a-z0-9]+-[a-z0-9]*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP IP6_overlay
+-
+  type: "plot"
+  title: "VPP Performance 78B-1t1c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'ENCAP' and 'NDRDISC' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-1t1c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-2t2c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'ENCAP' and 'NDRDISC' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-2t2c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-1t1c-ethip6[a-z0-9]+-[a-z0-9]*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'ENCAP' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-1t1c-ethip6[a-z0-9]+-[a-z0-9]*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 78B-2t2c-ethip6[a-z0-9]+-[a-z0-9]*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'ENCAP' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "78B-2t2c-ethip6[a-z0-9]+-[a-z0-9]*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP VM VHOST
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-.*vhost.*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-vhost-sel1-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '1T1C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-.*vhost.*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-vhost-sel1-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '2T2C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-.*vhost.*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-vhost-sel1-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-.*vhost.*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-vhost-sel1-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP VM VHOST SELECTION
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-.*vhost.*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-vhost-sel2-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-.*vhost.*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-vhost-sel2-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-.*vhost.*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-vhost-sel2-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-.*vhost.*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-vhost-sel2-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-.*vhost.*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# VPP CRYPTO
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-.*ipsec.*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ipsechw-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and not 'VHOST' and 'IP4FWD' and 'NDRDISC' and '1T1C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-.*ipsec.*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-.*ipsec.*-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ipsechw-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and not 'VHOST' and 'IP4FWD' and 'NDRDISC' and '2T2C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-.*ipsec.*-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-.*ipsec.*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ipsechw-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and not 'VHOST' and 'IP4FWD' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-.*ipsec.*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-.*ipsec.*-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ipsechw-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and not 'VHOST' and 'IP4FWD' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-.*ipsec.*-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# DPDK
+-
+  type: "plot"
+  title: "DPDK Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-1t1c-l2-ndrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "DPDK Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-2t2c-l2-ndrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "DPDK Performance 64B-1t1c-ethip4-ip4base-l3fwd-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-1t1c-ipv4-ndrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and 'IP4FWD'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-ethip4-ip4base-l3fwd-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "DPDK Performance 64B-2t2c-ethip4-ip4base-l3fwd-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-2t2c-ipv4-ndrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '2T2C' and 'IP4FWD'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-ethip4-ip4base-l3fwd-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "DPDK Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-1t1c-l2-pdrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "DPDK Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-2t2c-l2-pdrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "DPDK Performance 64B-1t1c-ethip4-ip4base-l3fwd-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-1t1c-ipv4-pdrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'IP4FWD'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-ethip4-ip4base-l3fwd-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "DPDK Performance 64B-2t2c-ethip4-ip4base-l3fwd-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-2t2c-ipv4-pdrdisc"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'IP4FWD'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-ethip4-ip4base-l3fwd-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# Plot latency
+
+# VPP L2 sel1
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel1-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-l2-sel1-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP L2 sel2
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-l2-sel2-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('FEATURE' and 'ACL50' and '10k_FLOWS') and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-l2-sel2-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('FEATURE' and 'ACL50' and '10k_FLOWS') and 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP IP4
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ethip4-ip4-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '1T1C' and 'IP4FWD' and not 'ACL1' and not 'ACL10' and not '100_FLOWS' and not '100k_FLOWS' and not 'IPSEC' and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ethip4-ip4-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '2T2C' and 'IP4FWD' and not 'ACL1' and not 'ACL10' and not '100_FLOWS' and not '100k_FLOWS' and not 'IPSEC' and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-ethip4-ip4[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP IP6
+-
+  type: "plot"
+  title: "VPP Latency 78B-1t1c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-ip6-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '1T1C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "78B-1t1c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 78B-2t2c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-ip6-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and ('BASE' or 'SCALE' or 'FEATURE') and 'NDRDISC' and '2T2C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "78B-2t2c-ethip6-ip6[a-z0-9]+-[a-z-]*ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP SRv6
+-
+  type: "plot"
+  title: "VPP Latency 78B-1t1c-ethip6*srv6*ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-srv6-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'FEATURE' and 'NDRDISC' and '1T1C' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "78B-1t1c-ethip6*srv6*ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 78B-2t2c-ethip6*srv6*ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-srv6-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'FEATURE' and 'NDRDISC' and '2T2C' and 'IP6FWD' and 'SRv6'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "78B-2t2c-ethip6*srv6*ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP IP4_overlay
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ethip4-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'ENCAP' and 'NDRDISC' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ethip4-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'ENCAP' and 'NDRDISC' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-ethip4[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP IP6_overlay
+-
+  type: "plot"
+  title: "VPP Latency 78B-1t1c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-1t1c-ethip6-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'ENCAP' and 'NDRDISC' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "78B-1t1c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 78B-2t2c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/78B-2t2c-ethip6-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'78B' and 'ENCAP' and 'NDRDISC' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "78B-2t2c-ethip6[a-z0-9]+-[a-z0-9]*-ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP VM VHOST
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-.*vhost.*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-vhost-sel1-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '1T1C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-.*vhost.*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-vhost-sel1-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '2T2C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD')"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP VM VHOST selection
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-.*vhost.*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-vhost-sel2-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-.*vhost.*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-vhost-sel2-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'NDRDISC' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'DOT1Q' and not '2VM'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-.*vhost.*-ndrdisc"
+    layout:
+      "plot-latency"
+
+# VPP CRYPTO
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-.*ipsec.*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-ipsechw-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and not 'VHOST' and 'IP4FWD' and 'NDRDISC' and '1T1C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-.*ipsec.*-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-.*ipsec.*-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-ipsechw-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and not 'VHOST' and 'IP4FWD' and 'NDRDISC' and '2T2C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN')"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-.*ipsec.*-ndrdisc"
+    layout:
+      "plot-latency"
+
+# DPDK
+-
+  type: "plot"
+  title: "DPDK Latency 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-1t1c-l2-ndrdisc-lat50"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "DPDK Latency 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-2t2c-l2-ndrdisc-lat50"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "DPDK Latency 64B-1t1c-ethip4-ip4base-l3fwd-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-1t1c-ipv4-ndrdisc-lat50"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and 'IP4FWD'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-ethip4-ip4base-l3fwd-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "DPDK Latency 64B-2t2c-ethip4-ip4base-l3fwd-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,DPDK]}/64B-2t2c-ipv4-ndrdisc-lat50"
+  data:
+    "plot-dpdk-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '2T2C' and 'IP4FWD'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-ethip4-ip4base-l3fwd-ndrdisc"
+    layout:
+      "plot-latency"
+
+# Ligato - Throughput
+
+# Container memif
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-container-memif-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-container-memif-ndrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-container-memif-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-container-memif-pdrdisc"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'BASE' and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# Container orchestrated
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-container-orchestrated-ndrdisc"
+  data:
+    "plot-ligato-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-container-orchestrated-ndrdisc"
+  data:
+    "plot-ligato-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-container-orchestrated-pdrdisc"
+  data:
+    "plot-ligato-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+    layout:
+      "plot-throughput"
+
+-
+  type: "plot"
+  title: "VPP Performance 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+  algorithm: "plot_performance_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-container-orchestrated-pdrdisc"
+  data:
+    "plot-ligato-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'PDRDISC' and not 'NDRDISC' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "throughput"
+  - "parent"
+  traces:
+    hoverinfo: "x+y"
+    boxpoints: "outliers"
+    whiskerwidth: 0
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-pdrdisc"
+    layout:
+      "plot-throughput"
+
+# Ligato - Latency
+
+# Container memif
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-container-memif-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-container-memif-ndrdisc-lat50"
+  data:
+    "plot-vpp-throughput-latency"
+  filter: "'64B' and 'BASE' and 'NDRDISC' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-latency"
+
+# Container orchestrated
+-
+  type: "plot"
+  title: "VPP Latency 64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-1t1c-container-orchestrated-ndrdisc-lat50"
+  data:
+    "plot-ligato-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-1t1c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-latency"
+
+-
+  type: "plot"
+  title: "VPP Latency 64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+  algorithm: "plot_latency_box"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/64B-2t2c-container-orchestrated-ndrdisc-lat50"
+  data:
+    "plot-ligato-throughput-latency"
+  filter: "'64B' and ('BASE' or 'SCALE') and 'NDRDISC' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+  parameters:
+  - "latency"
+  - "parent"
+  traces:
+    boxmean: False
+  layout:
+    title: "64B-2t2c-(eth|dot1q|dot1ad)-(l2xcbase|l2bdbasemaclrn)-memif-ndrdisc"
+    layout:
+      "plot-latency"
diff --git a/resources/tools/presentation/new/specification_CPTA.yaml b/resources/tools/presentation/new/specification_CPTA.yaml
new file mode 100644 (file)
index 0000000..971d7c6
--- /dev/null
@@ -0,0 +1,1287 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is the specification of parameters for "Continuous Performance Trending
+# and Analysis" feature provided by PAL.
+
+-
+  type: "environment"
+
+  paths:
+    # Top level directories:
+    ## Working directory
+    DIR[WORKING]: "_tmp"
+    ## Build directories
+    DIR[BUILD,HTML]: "_build"
+    ## Static .rst files
+    DIR[RST]: "../../../../docs/cpta"
+
+    # Static html content
+    DIR[STATIC]: "{DIR[BUILD,HTML]}/_static"
+    DIR[STATIC,VPP]: "{DIR[STATIC]}/vpp"
+    # DIR[STATIC,DPDK]: "{DIR[STATIC]}/dpdk"
+    DIR[STATIC,ARCH]: "{DIR[STATIC]}/archive"
+
+    # Working directories
+    ## Input data files (.zip, .xml)
+    DIR[WORKING,DATA]: "{DIR[WORKING]}/data"
+    ## Static source files from git
+    DIR[WORKING,SRC]: "{DIR[WORKING]}/src"
+    DIR[WORKING,SRC,STATIC]: "{DIR[WORKING,SRC]}/_static"
+
+    # .css patch file
+    DIR[CSS_PATCH_FILE]: "{DIR[STATIC]}/theme_overrides.css"
+    DIR[CSS_PATCH_FILE2]: "{DIR[WORKING,SRC,STATIC]}/theme_overrides.css"
+
+  urls:
+    URL[JENKINS,CSIT]: "https://jenkins.fd.io/view/csit/job"
+    URL[NEXUS,LOG]: "https://logs.fd.io/production/vex-yul-rot-jenkins-1"
+    URL[NEXUS]: "https://docs.fd.io/csit"
+    DIR[NEXUS]: "trending/_static/archive"
+
+  make-dirs:
+  # List the directories which are created while preparing the environment.
+  # All directories MUST be defined in "paths" section.
+  - "DIR[WORKING,DATA]"
+  - "DIR[WORKING,SRC,STATIC]"
+  - "DIR[BUILD,HTML]"
+  - "DIR[STATIC,VPP]"
+  - "DIR[STATIC,ARCH]"
+  build-dirs:
+  # List the directories where the results (build) is stored.
+  # All directories MUST be defined in "paths" section.
+  - "DIR[BUILD,HTML]"
+
+-
+  type: "configuration"
+
+  data-sets:
+    plot-performance-trending:
+      csit-vpp-perf-mrr-daily-master:
+#      - 15
+#      - 20
+#      - 25
+#      - 30
+#      - 35
+#      - 40
+#      - 45
+#      - 50
+#      - 55
+#      - 60
+#      - 65
+#      - 70
+#      - 75
+#      - 80
+#      - 85
+#      - 90
+#      - 95
+        start: 15
+        end: "lastCompletedBuild" # "lastSuccessfulBuild"  # take all from the 'start'
+
+  plot-layouts:
+    plot-cpta:
+      title: ""
+      autosize: False
+      showlegend: True
+      width: 1100
+      height: 800
+      yaxis:
+        showticklabels: True
+        tickformat: ".3s"
+        title: "Throughput [pps]"
+        hoverformat: ".4s"
+        range: []
+        gridcolor: "rgb(238, 238, 238)"
+        linecolor: "rgb(238, 238, 238)"
+        showline: True
+        zeroline: False
+        tickcolor: "rgb(238, 238, 238)"
+        linewidth: 1
+        showgrid: True
+      xaxis:
+        title: '<a href="https://jenkins.fd.io/view/csit/job/csit-vpp-perf-mrr-daily-master" target="_blank">csit-vpp-perf-mrr-daily-master-build</a>/<a href="https://nexus.fd.io/content/repositories/fd.io.master.ubuntu.xenial.main/io/fd/vpp/vpp/" target="_blank">vpp-build</a>'
+        type: "date"
+        autorange: True
+        fixedrange: False
+        showgrid: True
+        gridcolor: "rgb(238, 238, 238)"
+        showline: True
+        linecolor: "rgb(238, 238, 238)"
+        zeroline: False
+        linewidth: 1
+        showticklabels: True
+        tickcolor: "rgb(238, 238, 238)"
+        autotick: True
+        tickformat: "%m%d"
+        rangeselector:
+          buttons:
+          - count: 14
+            label: "2w"
+            step: "day"
+            stepmode: "backward"
+          - count: 1
+            label: "1m"
+            step: "month"
+            stepmode: "backward"
+          - count: 2
+            label: "2m"
+            step: "month"
+            stepmode: "backward"
+          - count: 3
+            label: "3m"
+            step: "month"
+            stepmode: "backward"
+          - step: "all"
+        # rangeslider: {}
+      margin:
+        r: 20
+        # b: 200
+        t: 5
+        l: 70
+      legend:
+        orientation: "h"
+        # y: -0.5
+        xanchor: "center"
+        traceorder: "normal"  # "grouped" does not work: bug https://github.com/plotly/plotly.js/issues/1913
+        tracegroupgap: 20
+        bordercolor: "rgb(238, 238, 238)"
+        # borderwidth: 1
+      hoverlabel:
+        namelength: -1
+
+-
+  type: "static"
+  src-path: "{DIR[RST]}"
+  dst-path: "{DIR[WORKING,SRC]}"
+
+-
+  type: "input"  # Ignored in debug mode
+#  general:
+#    file-name: "robot-plugin.zip"
+#    file-format: ".zip"
+#    download-path: "{job}/{build}/robot/report/*zip*/{filename}"
+#    extract: "robot-plugin/output.xml"
+  general:
+    file-name: "output.xml.gz"
+    file-format: ".gz"
+    download-path: "{job}/{build}/archives/{filename}"
+    extract: "output.xml"
+  builds:
+    csit-vpp-perf-mrr-daily-master:
+#    - 15
+#    - 20
+#    - 25
+#    - 30
+#    - 35
+#    - 40
+#    - 45
+#    - 50
+#    - 55
+#    - 60
+#    - 65
+#    - 70
+#    - 75
+#    - 80
+#    - 85
+#    - 90
+#    - 95
+      start: 15
+      end: "lastCompletedBuild"  # take all from the 'start'
+
+-
+  type: "output"
+  output:
+#   "report"
+    "CPTA"  # Continuous Performance Trending and Analysis
+  format:
+    html:
+    - full
+    pdf:
+    - minimal
+
+################################################################################
+###                               T A B L E S                                ###
+################################################################################
+
+-
+  type: "table"
+  title: "Performance trending dashboard"
+  algorithm: "table_performance_trending_dashboard"
+  output-file-ext: ".csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-1t1c"
+  data: "plot-performance-trending"
+  filter: "'MRR' and '1T1C'"
+  parameters:
+  - "name"
+  - "parent"
+  - "result"
+  ignore-list:
+  - "tests.vpp.perf.l2.10ge2p1x520-eth-l2bdscale1mmaclrn-mrr.tc01-64b-1t1c-eth-l2bdscale1mmaclrn-ndrdisc"
+  outlier-const: 1.5
+  window: 14
+  evaluated-window: 14
+  long-trend-window: 180
+
+-
+  type: "table"
+  title: "Performance trending dashboard"
+  algorithm: "table_performance_trending_dashboard"
+  output-file-ext: ".csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-2t2c"
+  data: "plot-performance-trending"
+  filter: "'MRR' and '2T2C'"
+  parameters:
+  - "name"
+  - "parent"
+  - "result"
+  ignore-list:
+  - "tests.vpp.perf.l2.10ge2p1x520-eth-l2bdscale1mmaclrn-mrr.tc05-64b-2t2c-eth-l2bdscale1mmaclrn-ndrdisc"
+  outlier-const: 1.5
+  window: 14
+  evaluated-window: 14
+  long-trend-window: 180
+
+-
+  type: "table"
+  title: "Performance trending dashboard"
+  algorithm: "table_performance_trending_dashboard"
+  output-file-ext: ".csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-4t4c"
+  data: "plot-performance-trending"
+  filter: "'MRR' and '4T4C'"
+  parameters:
+  - "name"
+  - "parent"
+  - "result"
+  ignore-list:
+  - "tests.vpp.perf.l2.10ge2p1x520-eth-l2bdscale1mmaclrn-mrr.tc09-64b-4t4c-eth-l2bdscale1mmaclrn-ndrdisc"
+  outlier-const: 1.5
+  window: 14
+  evaluated-window: 14
+  long-trend-window: 180
+
+-
+  type: "table"
+  title: "HTML performance trending dashboard 1t1c"
+  algorithm: "table_performance_trending_dashboard_html"
+  input-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-1t1c.csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-1t1c.rst"
+
+-
+  type: "table"
+  title: "HTML performance trending dashboard 2t2c"
+  algorithm: "table_performance_trending_dashboard_html"
+  input-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-2t2c.csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-2t2c.rst"
+
+-
+  type: "table"
+  title: "HTML performance trending dashboard 4t4c"
+  algorithm: "table_performance_trending_dashboard_html"
+  input-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-4t4c.csv"
+  output-file: "{DIR[STATIC,VPP]}/performance-trending-dashboard-4t4c.rst"
+
+
+################################################################################
+###                                 C P T A                                  ###
+################################################################################
+
+# Plots VPP Continuous Performance Trending and Analysis
+-
+  type: "cpta"
+  title: "Continuous Performance Trending and Analysis"
+  algorithm: "cpta"
+  output-file-type: ".html"
+  output-file: "{DIR[STATIC,VPP]}/cpta"
+  data: "plot-performance-trending"
+  plots:
+
+# L2 - x520
+
+    - title: "VPP 1T1C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '4T4C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 1T1C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-feature-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'FEATURE' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-feature-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'FEATURE' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-feature-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'FEATURE' and '4T4C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# L2 - xl710
+
+    - title: "VPP 1T1C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '4T4C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# L2 - x710
+
+    - title: "VPP 1T1C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '4T4C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 1T1C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-feature-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'FEATURE' and '1T1C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-feature-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'FEATURE' and '2T2C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 64B Packet Throughput - Trending"
+      output-file-name: "l2-feature-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'FEATURE' and '4T4C' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST' and not 'MEMIF'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv4 - x520
+
+    - title: "VPP 1T1C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '1T1C' and 'IP4FWD' and not 'FEATURE' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '2T2C' and 'IP4FWD' and not 'FEATURE' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '4T4C' and 'IP4FWD' and not 'FEATURE' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 1T1C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-feature-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'FEATURE' and '1T1C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-feature-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'FEATURE' and '2T2C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-feature-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'FEATURE' and '4T4C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv4 - xl710
+
+    - title: "VPP 1T1C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and ('BASE' or 'SCALE' or 'FEATURE') and '1T1C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and ('BASE' or 'SCALE' or 'FEATURE') and '2T2C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and ('BASE' or 'SCALE' or 'FEATURE') and '4T4C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv4 - x710
+
+    - title: "VPP 1T1C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '1T1C' and 'IP4FWD' and not 'FEATURE' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '2T2C' and 'IP4FWD' and not 'FEATURE' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and ('BASE' or 'SCALE') and '4T4C' and 'IP4FWD' and not 'FEATURE' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 1T1C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-feature-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'FEATURE' and '1T1C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-feature-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'FEATURE' and '2T2C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv4 64B Packet Throughput - Trending"
+      output-file-name: "ip4-feature-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'FEATURE' and '4T4C' and 'IP4FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv4 Tunnels - x520
+
+    - title: "VPP 1T1C IPv4 Tunnels 64B Packet Throughput - Trending"
+      output-file-name: "ip4-tunnels-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'ENCAP' and 'MRR' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv4 Tunnels 64B Packet Throughput - Trending"
+      output-file-name: "ip4-tunnels-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'ENCAP' and 'MRR' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv4 Tunnels 64B Packet Throughput - Trending"
+      output-file-name: "ip4-tunnels-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'ENCAP' and 'MRR' and '4T4C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv4 Tunnels - x710
+
+    - title: "VPP 1T1C IPv4 Tunnels 64B Packet Throughput - Trending"
+      output-file-name: "ip4-tunnels-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'ENCAP' and 'MRR' and '1T1C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv4 Tunnels 64B Packet Throughput - Trending"
+      output-file-name: "ip4-tunnels-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'ENCAP' and 'MRR' and '2T2C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv4 Tunnels 64B Packet Throughput - Trending"
+      output-file-name: "ip4-tunnels-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'ENCAP' and 'MRR' and '4T4C' and ('VXLAN' or 'VXLANGPE' or 'LISP' or 'LISPGPE' or 'GRE') and not 'VHOST' and not 'IPSECHW'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv6 - x520
+
+    - title: "VPP 1T1C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '1T1C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '2T2C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '4T4C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST' and not 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv6 - xl710
+
+    - title: "VPP 1T1C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '1T1C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '2T2C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '4T4C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPv6 - x710
+
+    - title: "VPP 1T1C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '1T1C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '2T2C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPv6 78B Packet Throughput - Trending"
+      output-file-name: "ip6-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '78B' and ('BASE' or 'SCALE' or 'FEATURE') and '4T4C' and 'IP6FWD' and not 'IPSEC' and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Container memif - x520, 64B
+
+    - title: "VPP 1T1C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'BASE' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'BASE' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and '64B' and 'BASE' and '4T4C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Container memif - x520, IMIX
+
+    - title: "VPP 1T1C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and 'IMIX' and 'BASE' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and 'IMIX' and 'BASE' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'MRR' and 'IMIX' and 'BASE' and '4T4C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Container memif - xl710, 64B
+
+    - title: "VPP 1T1C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and 'BASE' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and 'BASE' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and '64B' and 'BASE' and '4T4C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Container memif - xl710, IMIX
+
+    - title: "VPP 1T1C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and 'IMIX' and 'BASE' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and 'IMIX' and 'BASE' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'MRR' and 'IMIX' and 'BASE' and '4T4C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Container memif - x710, 64B
+
+    - title: "VPP 1T1C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'BASE' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'BASE' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 Container memif 64B Packet Throughput - Trending"
+      output-file-name: "container-memif-l2-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and '64B' and 'BASE' and '4T4C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Container memif - x520, IMIX
+
+    - title: "VPP 1T1C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and 'IMIX' and 'BASE' and '1T1C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and 'IMIX' and 'BASE' and '2T2C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C L2 Container memif IMIX Packet Throughput - Trending"
+      output-file-name: "container-memif-imix-l2-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'MRR' and 'IMIX' and 'BASE' and '4T4C' and 'MEMIF' and ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x520, ethip4, 64B
+
+    - title: "VPP 1T1C VM vhost ethip4 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-ethip4-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '1T1C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost ethip4 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-ethip4-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '2T2C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost ethip4 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-ethip4-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '4T4C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x520, ethip4, IMIX
+
+    - title: "VPP 1T1C VM vhost ethip4 IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-ethip4-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '1T1C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost ethip4 IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-ethip4-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '2T2C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost ethip4 IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-ethip4-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '4T4C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x520, eth, 64B
+
+    - title: "VPP 1T1C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '4T4C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x520, eth, IMIX
+
+    - title: "VPP 1T1C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '4T4C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - xl710, eth, 64B
+
+    - title: "VPP 1T1C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and '64B' and 'MRR' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and '64B' and 'MRR' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and '64B' and 'MRR' and '4T4C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - xl710, eth, IMIX
+
+    - title: "VPP 1T1C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'IMIX' and 'MRR' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'IMIX' and 'MRR' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and 'IMIX' and 'MRR' and '4T4C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x710, ethip4, 64B
+
+    - title: "VPP 1T1C VM vhost ethip4 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-ethip4-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '1T1C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost ethip4 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-ethip4-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '2T2C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost ethip4 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-ethip4-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '4T4C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x710, ethip4, IMIX
+
+    - title: "VPP 1T1C VM vhost ethip4 IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-ethip4-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '1T1C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost ethip4 IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-ethip4-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '2T2C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost ethip4 IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-ethip4-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '4T4C' and 'VHOST' and not ('L2BDMACSTAT' or 'L2BDMACLRN' or 'L2XCFWD') and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x710, eth, 64B
+
+    - title: "VPP 1T1C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost eth 64B Packet Throughput - Trending"
+      output-file-name: "vm-vhost-eth-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '4T4C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# VM vhost - x710, eth, IMIX
+
+    - title: "VPP 1T1C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '1T1C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '2T2C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C VM vhost eth IMIX Packet Throughput - Trending"
+      output-file-name: "vm-vhost-imix-eth-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '4T4C' and 'VHOST' and not 'VXLAN' and not 'IP4FWD' and not 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# IPSec
+
+    - title: "VPP 1T1C IPSec 64B Packet Throughput - Trending"
+      output-file-name: "ipsec-1t1c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and '64B' and 'IP4FWD' and 'MRR' and '1T1C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C IPSec 64B Packet Throughput - Trending"
+      output-file-name: "ipsec-2t2c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and '64B' and 'IP4FWD' and 'MRR' and '2T2C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C IPSec 64B Packet Throughput - Trending"
+      output-file-name: "ipsec-4t4c-xl710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-XL710' and '64B' and 'IP4FWD' and 'MRR' and '4T4C' and 'IPSECHW' and ('IPSECTRAN' or 'IPSECTUN') and not 'VHOST'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# SRv6 - x520
+
+    - title: "VPP 1T1C SRv6 78B MRR Trending"
+      output-file-name: "srv6-78b-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '78B' and 'MRR' and '1T1C' and 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C SRv6 78B MRR Trending"
+      output-file-name: "srv6-78b-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '78B' and 'MRR' and '2T2C' and 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C SRv6 78B MRR Trending"
+      output-file-name: "srv6-78b-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '78B' and 'MRR' and '4T4C' and 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 1T1C SRv6 IMIX MRR Trending"
+      output-file-name: "srv6-imix-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '1T1C' and 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C SRv6 IMIX MRR Trending"
+      output-file-name: "srv6-imix-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '2T2C' and 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C SRv6 IMIX MRR Trending"
+      output-file-name: "srv6-imix-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '4T4C' and 'SRv6'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Link Bonding - x520
+
+    - title: "VPP 1T1C Link Bonding 64B MRR Trending"
+      output-file-name: "lb-64b-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '1T1C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C Link Bonding 64B MRR Trending"
+      output-file-name: "lb-64b-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '2T2C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C Link Bonding 64B MRR Trending"
+      output-file-name: "lb-64b-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and '64B' and 'MRR' and '4T4C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 1T1C Link Bonding IMIX MRR Trending"
+      output-file-name: "lb-imix-1t1c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '1T1C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C Link Bonding IMIX MRR Trending"
+      output-file-name: "lb-imix-2t2c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '2T2C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C Link Bonding IMIX MRR Trending"
+      output-file-name: "lb-imix-4t4c-x520"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X520-DA2' and 'IMIX' and 'MRR' and '4T4C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+# Link Bonding - x710
+
+    - title: "VPP 1T1C Link Bonding 64B MRR Trending"
+      output-file-name: "lb-64b-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '1T1C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C Link Bonding 64B MRR Trending"
+      output-file-name: "lb-64b-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '2T2C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C Link Bonding 64B MRR Trending"
+      output-file-name: "lb-64b-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and '64B' and 'MRR' and '4T4C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 1T1C Link Bonding IMIX MRR Trending"
+      output-file-name: "lb-imix-1t1c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '1T1C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 2T2C Link Bonding IMIX MRR Trending"
+      output-file-name: "lb-imix-2t2c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '2T2C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
+
+    - title: "VPP 4T4C Link Bonding IMIX MRR Trending"
+      output-file-name: "lb-imix-4t4c-x710"
+      data: "plot-performance-trending"
+      filter: "'NIC_Intel-X710' and 'IMIX' and 'MRR' and '4T4C' and 'LBOND'"
+      parameters:
+      - "result"
+      layout: "plot-cpta"
diff --git a/resources/tools/presentation/new/specification_parser.py b/resources/tools/presentation/new/specification_parser.py
new file mode 100644 (file)
index 0000000..ebd8453
--- /dev/null
@@ -0,0 +1,626 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Specification
+
+Parsing of the specification YAML file.
+"""
+
+
+import logging
+from yaml import load, YAMLError
+from pprint import pformat
+
+from errors import PresentationError
+from utils import get_last_successful_build_number
+from utils import get_last_completed_build_number
+
+
+class Specification(object):
+    """Specification of Presentation and analytics layer.
+
+    - based on specification specified in the specification YAML file
+    - presentation and analytics layer is model driven
+    """
+
+    # Tags are used in specification YAML file and replaced while the file is
+    # parsed.
+    TAG_OPENER = "{"
+    TAG_CLOSER = "}"
+
+    def __init__(self, cfg_file):
+        """Initialization.
+
+        :param cfg_file: File handler for the specification YAML file.
+        :type cfg_file: BinaryIO
+        """
+        self._cfg_file = cfg_file
+        self._cfg_yaml = None
+
+        self._specification = {"environment": dict(),
+                               "configuration": dict(),
+                               "static": dict(),
+                               "input": dict(),
+                               "output": dict(),
+                               "tables": list(),
+                               "plots": list(),
+                               "files": list(),
+                               "cpta": dict()}
+
+    @property
+    def specification(self):
+        """Getter - specification.
+
+        :returns: Specification.
+        :rtype: dict
+        """
+        return self._specification
+
+    @property
+    def environment(self):
+        """Getter - environment.
+
+        :returns: Environment specification.
+        :rtype: dict
+        """
+        return self._specification["environment"]
+
+    @property
+    def configuration(self):
+        """Getter - configuration.
+
+        :returns: Configuration of PAL.
+        :rtype: dict
+        """
+        return self._specification["configuration"]
+
+    @property
+    def static(self):
+        """Getter - static content.
+
+        :returns: Static content specification.
+        :rtype: dict
+        """
+        return self._specification["static"]
+
+    @property
+    def is_debug(self):
+        """Getter - debug mode
+
+        :returns: True if debug mode is on, otherwise False.
+        :rtype: bool
+        """
+
+        try:
+            if self.environment["configuration"]["CFG[DEBUG]"] == 1:
+                return True
+            else:
+                return False
+        except KeyError:
+            return False
+
+    @property
+    def input(self):
+        """Getter - specification - inputs.
+        - jobs and builds.
+
+        :returns: Inputs.
+        :rtype: dict
+        """
+        return self._specification["input"]
+
+    @property
+    def builds(self):
+        """Getter - builds defined in specification.
+
+        :returns: Builds defined in the specification.
+        :rtype: dict
+        """
+        return self.input["builds"]
+
+    @property
+    def output(self):
+        """Getter - specification - output formats and versions to be generated.
+        - formats: html, pdf
+        - versions: full, ...
+
+        :returns: Outputs to be generated.
+        :rtype: dict
+        """
+        return self._specification["output"]
+
+    @property
+    def tables(self):
+        """Getter - tables to be generated.
+
+        :returns: List of specifications of tables to be generated.
+        :rtype: list
+        """
+        return self._specification["tables"]
+
+    @property
+    def plots(self):
+        """Getter - plots to be generated.
+
+        :returns: List of specifications of plots to be generated.
+        :rtype: list
+        """
+        return self._specification["plots"]
+
+    @property
+    def files(self):
+        """Getter - files to be generated.
+
+        :returns: List of specifications of files to be generated.
+        :rtype: list
+        """
+        return self._specification["files"]
+
+    @property
+    def cpta(self):
+        """Getter - Continuous Performance Trending and Analysis to be
+        generated.
+
+        :returns: List of specifications of Continuous Performance Trending and
+        Analysis to be generated.
+        :rtype: list
+        """
+        return self._specification["cpta"]
+
+    def set_input_state(self, job, build_nr, state):
+        """Set the state of input
+
+        :param job:
+        :param build_nr:
+        :param state:
+        :return:
+        """
+
+        try:
+            for build in self._specification["input"]["builds"][job]:
+                if build["build"] == build_nr:
+                    build["status"] = state
+                    break
+            else:
+                raise PresentationError("Build '{}' is not defined for job '{}'"
+                                        " in specification file.".
+                                        format(build_nr, job))
+        except KeyError:
+            raise PresentationError("Job '{}' and build '{}' is not defined in "
+                                    "specification file.".format(job, build_nr))
+
+    def set_input_file_name(self, job, build_nr, file_name):
+        """Set the state of input
+
+        :param job:
+        :param build_nr:
+        :param file_name:
+        :return:
+        """
+
+        try:
+            for build in self._specification["input"]["builds"][job]:
+                if build["build"] == build_nr:
+                    build["file-name"] = file_name
+                    break
+            else:
+                raise PresentationError("Build '{}' is not defined for job '{}'"
+                                        " in specification file.".
+                                        format(build_nr, job))
+        except KeyError:
+            raise PresentationError("Job '{}' and build '{}' is not defined in "
+                                    "specification file.".format(job, build_nr))
+
+    def _get_build_number(self, job, build_type):
+        """Get the number of the job defined by its name:
+         - lastSuccessfulBuild
+         - lastCompletedBuild
+
+        :param job: Job name.
+        :param build_type: Build type:
+         - lastSuccessfulBuild
+         - lastCompletedBuild
+        :type job" str
+        :raises PresentationError: If it is not possible to get the build
+        number.
+        :returns: The build number.
+        :rtype: int
+        """
+
+        # defined as a range <start, end>
+        if build_type == "lastSuccessfulBuild":
+            # defined as a range <start, lastSuccessfulBuild>
+            ret_code, build_nr, _ = get_last_successful_build_number(
+                self.environment["urls"]["URL[JENKINS,CSIT]"], job)
+        elif build_type == "lastCompletedBuild":
+            # defined as a range <start, lastCompletedBuild>
+            ret_code, build_nr, _ = get_last_completed_build_number(
+                self.environment["urls"]["URL[JENKINS,CSIT]"], job)
+        else:
+            raise PresentationError("Not supported build type: '{0}'".
+                                    format(build_type))
+        if ret_code != 0:
+            raise PresentationError("Not possible to get the number of the "
+                                    "build number.")
+        try:
+            build_nr = int(build_nr)
+            return build_nr
+        except ValueError as err:
+            raise PresentationError("Not possible to get the number of the "
+                                    "build number.\nReason: {0}".format(err))
+
+    def _get_type_index(self, item_type):
+        """Get index of item type (environment, input, output, ...) in
+        specification YAML file.
+
+        :param item_type: Item type: Top level items in specification YAML file,
+        e.g.: environment, input, output.
+        :type item_type: str
+        :returns: Index of the given item type.
+        :rtype: int
+        """
+
+        index = 0
+        for item in self._cfg_yaml:
+            if item["type"] == item_type:
+                return index
+            index += 1
+        return None
+
+    def _find_tag(self, text):
+        """Find the first tag in the given text. The tag is enclosed by the
+        TAG_OPENER and TAG_CLOSER.
+
+        :param text: Text to be searched.
+        :type text: str
+        :returns: The tag, or None if not found.
+        :rtype: str
+        """
+        try:
+            start = text.index(self.TAG_OPENER)
+            end = text.index(self.TAG_CLOSER, start + 1) + 1
+            return text[start:end]
+        except ValueError:
+            return None
+
+    def _replace_tags(self, data, src_data=None):
+        """Replace tag(s) in the data by their values.
+
+        :param data: The data where the tags will be replaced by their values.
+        :param src_data: Data where the tags are defined. It is dictionary where
+        the key is the tag and the value is the tag value. If not given, 'data'
+        is used instead.
+        :type data: str or dict
+        :type src_data: dict
+        :returns: Data with the tags replaced.
+        :rtype: str or dict
+        :raises: PresentationError if it is not possible to replace the tag or
+        the data is not the supported data type (str, dict).
+        """
+
+        if src_data is None:
+            src_data = data
+
+        if isinstance(data, str):
+            tag = self._find_tag(data)
+            if tag is not None:
+                data = data.replace(tag, src_data[tag[1:-1]])
+
+        elif isinstance(data, dict):
+            counter = 0
+            for key, value in data.items():
+                tag = self._find_tag(value)
+                if tag is not None:
+                    try:
+                        data[key] = value.replace(tag, src_data[tag[1:-1]])
+                        counter += 1
+                    except KeyError:
+                        raise PresentationError("Not possible to replace the "
+                                                "tag '{}'".format(tag))
+            if counter:
+                self._replace_tags(data, src_data)
+        else:
+            raise PresentationError("Replace tags: Not supported data type.")
+
+        return data
+
+    def _parse_env(self):
+        """Parse environment specification in the specification YAML file.
+        """
+
+        logging.info("Parsing specification file: environment ...")
+
+        idx = self._get_type_index("environment")
+        if idx is None:
+            return None
+
+        try:
+            self._specification["environment"]["configuration"] = \
+                self._cfg_yaml[idx]["configuration"]
+        except KeyError:
+            self._specification["environment"]["configuration"] = None
+
+        try:
+            self._specification["environment"]["paths"] = \
+                self._replace_tags(self._cfg_yaml[idx]["paths"])
+        except KeyError:
+            self._specification["environment"]["paths"] = None
+
+        try:
+            self._specification["environment"]["urls"] = \
+                self._replace_tags(self._cfg_yaml[idx]["urls"])
+        except KeyError:
+            self._specification["environment"]["urls"] = None
+
+        try:
+            self._specification["environment"]["make-dirs"] = \
+                self._cfg_yaml[idx]["make-dirs"]
+        except KeyError:
+            self._specification["environment"]["make-dirs"] = None
+
+        try:
+            self._specification["environment"]["remove-dirs"] = \
+                self._cfg_yaml[idx]["remove-dirs"]
+        except KeyError:
+            self._specification["environment"]["remove-dirs"] = None
+
+        try:
+            self._specification["environment"]["build-dirs"] = \
+                self._cfg_yaml[idx]["build-dirs"]
+        except KeyError:
+            self._specification["environment"]["build-dirs"] = None
+
+        logging.info("Done.")
+
+    def _parse_configuration(self):
+        """Parse configuration of PAL in the specification YAML file.
+        """
+
+        logging.info("Parsing specification file: configuration ...")
+
+        idx = self._get_type_index("configuration")
+        if idx is None:
+            logging.warning("No configuration information in the specification "
+                            "file.")
+            return None
+
+        try:
+            self._specification["configuration"] = self._cfg_yaml[idx]
+
+        except KeyError:
+            raise PresentationError("No configuration defined.")
+
+        # Data sets: Replace ranges by lists
+        for set_name, data_set in self.configuration["data-sets"].items():
+            for job, builds in data_set.items():
+                if builds:
+                    if isinstance(builds, dict):
+                        build_nr = builds.get("end", None)
+                        try:
+                            build_nr = int(build_nr)
+                        except ValueError:
+                            # defined as a range <start, build_type>
+                            build_nr = self._get_build_number(job, build_nr)
+                        builds = [x for x in range(builds["start"], build_nr+1)]
+                        self.configuration["data-sets"][set_name][job] = builds
+        logging.info("Done.")
+
+    def _parse_input(self):
+        """Parse input specification in the specification YAML file.
+
+        :raises: PresentationError if there are no data to process.
+        """
+
+        logging.info("Parsing specification file: input ...")
+
+        idx = self._get_type_index("input")
+        if idx is None:
+            raise PresentationError("No data to process.")
+
+        try:
+            for key, value in self._cfg_yaml[idx]["general"].items():
+                self._specification["input"][key] = value
+            self._specification["input"]["builds"] = dict()
+
+            for job, builds in self._cfg_yaml[idx]["builds"].items():
+                if builds:
+                    if isinstance(builds, dict):
+                        build_nr = builds.get("end", None)
+                        try:
+                            build_nr = int(build_nr)
+                        except ValueError:
+                            # defined as a range <start, build_type>
+                            build_nr = self._get_build_number(job, build_nr)
+                        builds = [x for x in range(builds["start"], build_nr+1)]
+                    self._specification["input"]["builds"][job] = list()
+                    for build in builds:
+                        self._specification["input"]["builds"][job]. \
+                            append({"build": build, "status": None})
+
+                else:
+                    logging.warning("No build is defined for the job '{}'. "
+                                    "Trying to continue without it.".
+                                    format(job))
+        except KeyError:
+            raise PresentationError("No data to process.")
+
+        logging.info("Done.")
+
+    def _parse_output(self):
+        """Parse output specification in the specification YAML file.
+
+        :raises: PresentationError if there is no output defined.
+        """
+
+        logging.info("Parsing specification file: output ...")
+
+        idx = self._get_type_index("output")
+        if idx is None:
+            raise PresentationError("No output defined.")
+
+        try:
+            self._specification["output"] = self._cfg_yaml[idx]
+        except (KeyError, IndexError):
+            raise PresentationError("No output defined.")
+
+        logging.info("Done.")
+
+    def _parse_static(self):
+        """Parse specification of the static content in the specification YAML
+        file.
+        """
+
+        logging.info("Parsing specification file: static content ...")
+
+        idx = self._get_type_index("static")
+        if idx is None:
+            logging.warning("No static content specified.")
+
+        for key, value in self._cfg_yaml[idx].items():
+            if isinstance(value, str):
+                try:
+                    self._cfg_yaml[idx][key] = self._replace_tags(
+                        value, self._specification["environment"]["paths"])
+                except KeyError:
+                    pass
+
+        self._specification["static"] = self._cfg_yaml[idx]
+
+        logging.info("Done.")
+
+    def _parse_elements(self):
+        """Parse elements (tables, plots) specification in the specification
+        YAML file.
+        """
+
+        logging.info("Parsing specification file: elements ...")
+
+        count = 1
+        for element in self._cfg_yaml:
+            try:
+                element["output-file"] = self._replace_tags(
+                    element["output-file"],
+                    self._specification["environment"]["paths"])
+            except KeyError:
+                pass
+
+            try:
+                element["input-file"] = self._replace_tags(
+                    element["input-file"],
+                    self._specification["environment"]["paths"])
+            except KeyError:
+                pass
+
+            # add data sets to the elements:
+            if isinstance(element.get("data", None), str):
+                data_set = element["data"]
+                try:
+                    element["data"] = self.configuration["data-sets"][data_set]
+                except KeyError:
+                    raise PresentationError("Data set {0} is not defined in "
+                                            "the configuration section.".
+                                            format(data_set))
+
+            if element["type"] == "table":
+                logging.info("  {:3d} Processing a table ...".format(count))
+                try:
+                    element["template"] = self._replace_tags(
+                        element["template"],
+                        self._specification["environment"]["paths"])
+                except KeyError:
+                    pass
+                self._specification["tables"].append(element)
+                count += 1
+
+            elif element["type"] == "plot":
+                logging.info("  {:3d} Processing a plot ...".format(count))
+
+                # Add layout to the plots:
+                layout = element["layout"].get("layout", None)
+                if layout is not None:
+                    element["layout"].pop("layout")
+                    try:
+                        for key, val in (self.configuration["plot-layouts"]
+                                         [layout].items()):
+                            element["layout"][key] = val
+                    except KeyError:
+                        raise PresentationError("Layout {0} is not defined in "
+                                                "the configuration section.".
+                                                format(layout))
+                self._specification["plots"].append(element)
+                count += 1
+
+            elif element["type"] == "file":
+                logging.info("  {:3d} Processing a file ...".format(count))
+                try:
+                    element["dir-tables"] = self._replace_tags(
+                        element["dir-tables"],
+                        self._specification["environment"]["paths"])
+                except KeyError:
+                    pass
+                self._specification["files"].append(element)
+                count += 1
+
+            elif element["type"] == "cpta":
+                logging.info("  {:3d} Processing Continuous Performance "
+                             "Trending and Analysis ...".format(count))
+
+                for plot in element["plots"]:
+                    # Add layout to the plots:
+                    layout = plot.get("layout", None)
+                    if layout is not None:
+                        try:
+                            plot["layout"] = \
+                                self.configuration["plot-layouts"][layout]
+                        except KeyError:
+                            raise PresentationError(
+                                "Layout {0} is not defined in the "
+                                "configuration section.".format(layout))
+                    # Add data sets:
+                    if isinstance(plot.get("data", None), str):
+                        data_set = plot["data"]
+                        try:
+                            plot["data"] = \
+                                self.configuration["data-sets"][data_set]
+                        except KeyError:
+                            raise PresentationError(
+                                "Data set {0} is not defined in "
+                                "the configuration section.".
+                                format(data_set))
+                self._specification["cpta"] = element
+                count += 1
+
+        logging.info("Done.")
+
+    def read_specification(self):
+        """Parse specification in the specification YAML file.
+
+        :raises: PresentationError if an error occurred while parsing the
+        specification file.
+        """
+        try:
+            self._cfg_yaml = load(self._cfg_file)
+        except YAMLError as err:
+            raise PresentationError(msg="An error occurred while parsing the "
+                                        "specification file.",
+                                    details=str(err))
+
+        self._parse_env()
+        self._parse_configuration()
+        self._parse_input()
+        self._parse_output()
+        self._parse_static()
+        self._parse_elements()
+
+        logging.debug("Specification: \n{}".
+                      format(pformat(self._specification)))
diff --git a/resources/tools/presentation/new/static_content.py b/resources/tools/presentation/new/static_content.py
new file mode 100644 (file)
index 0000000..a02330c
--- /dev/null
@@ -0,0 +1,60 @@
+# Copyright (c) 2017 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Static content
+
+Process the static content stored in the git.
+"""
+
+import logging
+
+from os import makedirs
+from os.path import isdir
+from shutil import rmtree, copytree, Error
+
+from errors import PresentationError
+
+
+def prepare_static_content(spec):
+    """Prepare the static content which is stored in the git.
+
+    :param spec: Specification read from the specification file.
+    :type spec: Specification
+    :raises PresentationError: If it is not possible to process the static
+    content.
+    """
+
+    src = spec.static.get("src-path", None)
+    dst = spec.static.get("dst-path", None)
+    if src is None or dst is None:
+        logging.warning("No static content specified, skipping")
+        return
+
+    # Copy all the static content to the build directory:
+    logging.info("Copying the static content ...")
+    logging.info("  Source:      {0}".format(src))
+    logging.info("  Destination: {0}".format(dst))
+
+    try:
+        if isdir(dst):
+            rmtree(dst)
+
+        copytree(src, dst)
+
+        makedirs(spec.environment["paths"]["DIR[WORKING,SRC,STATIC]"])
+
+    except (Error, OSError) as err:
+        raise PresentationError("Not possible to process the static content.",
+                                str(err))
+
+    logging.info("Done.")
diff --git a/resources/tools/presentation/new/utils.py b/resources/tools/presentation/new/utils.py
new file mode 100644 (file)
index 0000000..83f4f62
--- /dev/null
@@ -0,0 +1,291 @@
+# Copyright (c) 2018 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""General purpose utilities.
+"""
+
+import multiprocessing
+import subprocess
+import numpy as np
+import pandas as pd
+import logging
+
+from os import walk, makedirs, environ
+from os.path import join, isdir
+from shutil import move, Error
+from math import sqrt
+
+from errors import PresentationError
+from jumpavg.BitCountingClassifier import BitCountingClassifier
+
+
+def mean(items):
+    """Calculate mean value from the items.
+
+    :param items: Mean value is calculated from these items.
+    :type items: list
+    :returns: MEan value.
+    :rtype: float
+    """
+
+    return float(sum(items)) / len(items)
+
+
+def stdev(items):
+    """Calculate stdev from the items.
+
+    :param items: Stdev is calculated from these items.
+    :type items: list
+    :returns: Stdev.
+    :rtype: float
+    """
+
+    avg = mean(items)
+    variance = [(x - avg) ** 2 for x in items]
+    stddev = sqrt(mean(variance))
+    return stddev
+
+
+def relative_change(nr1, nr2):
+    """Compute relative change of two values.
+
+    :param nr1: The first number.
+    :param nr2: The second number.
+    :type nr1: float
+    :type nr2: float
+    :returns: Relative change of nr1.
+    :rtype: float
+    """
+
+    return float(((nr2 - nr1) / nr1) * 100)
+
+
+def get_files(path, extension=None, full_path=True):
+    """Generates the list of files to process.
+
+    :param path: Path to files.
+    :param extension: Extension of files to process. If it is the empty string,
+        all files will be processed.
+    :param full_path: If True, the files with full path are generated.
+    :type path: str
+    :type extension: str
+    :type full_path: bool
+    :returns: List of files to process.
+    :rtype: list
+    """
+
+    file_list = list()
+    for root, _, files in walk(path):
+        for filename in files:
+            if extension:
+                if filename.endswith(extension):
+                    if full_path:
+                        file_list.append(join(root, filename))
+                    else:
+                        file_list.append(filename)
+            else:
+                file_list.append(join(root, filename))
+
+    return file_list
+
+
+def get_rst_title_char(level):
+    """Return character used for the given title level in rst files.
+
+    :param level: Level of the title.
+    :type: int
+    :returns: Character used for the given title level in rst files.
+    :rtype: str
+    """
+    chars = ('=', '-', '`', "'", '.', '~', '*', '+', '^')
+    if level < len(chars):
+        return chars[level]
+    else:
+        return chars[-1]
+
+
+def execute_command(cmd):
+    """Execute the command in a subprocess and log the stdout and stderr.
+
+    :param cmd: Command to execute.
+    :type cmd: str
+    :returns: Return code of the executed command.
+    :rtype: int
+    """
+
+    env = environ.copy()
+    proc = subprocess.Popen(
+        [cmd],
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        shell=True,
+        env=env)
+
+    stdout, stderr = proc.communicate()
+
+    if stdout:
+        logging.info(stdout)
+    if stderr:
+        logging.info(stderr)
+
+    if proc.returncode != 0:
+        logging.error("    Command execution failed.")
+    return proc.returncode, stdout, stderr
+
+
+def get_last_successful_build_number(jenkins_url, job_name):
+    """Get the number of the last successful build of the given job.
+
+    :param jenkins_url: Jenkins URL.
+    :param job_name: Job name.
+    :type jenkins_url: str
+    :type job_name: str
+    :returns: The build number as a string.
+    :rtype: str
+    """
+
+    url = "{}/{}/lastSuccessfulBuild/buildNumber".format(jenkins_url, job_name)
+    cmd = "wget -qO- {url}".format(url=url)
+
+    return execute_command(cmd)
+
+
+def get_last_completed_build_number(jenkins_url, job_name):
+    """Get the number of the last completed build of the given job.
+
+    :param jenkins_url: Jenkins URL.
+    :param job_name: Job name.
+    :type jenkins_url: str
+    :type job_name: str
+    :returns: The build number as a string.
+    :rtype: str
+    """
+
+    url = "{}/{}/lastCompletedBuild/buildNumber".format(jenkins_url, job_name)
+    cmd = "wget -qO- {url}".format(url=url)
+
+    return execute_command(cmd)
+
+
+def archive_input_data(spec):
+    """Archive the report.
+
+    :param spec: Specification read from the specification file.
+    :type spec: Specification
+    :raises PresentationError: If it is not possible to archive the input data.
+    """
+
+    logging.info("    Archiving the input data files ...")
+
+    extension = spec.input["file-format"]
+    data_files = get_files(spec.environment["paths"]["DIR[WORKING,DATA]"],
+                           extension=extension)
+    dst = spec.environment["paths"]["DIR[STATIC,ARCH]"]
+    logging.info("      Destination: {0}".format(dst))
+
+    try:
+        if not isdir(dst):
+            makedirs(dst)
+
+        for data_file in data_files:
+            logging.info("      Moving the file: {0} ...".format(data_file))
+            move(data_file, dst)
+
+    except (Error, OSError) as err:
+        raise PresentationError("Not possible to archive the input data.",
+                                str(err))
+
+    logging.info("    Done.")
+
+
+def classify_anomalies(data):
+    """Process the data and return anomalies and trending values.
+
+    Gathers data into groups with common trend value.
+    Decorates first value in the group to be an outlier, regression,
+    normal or progression.
+
+    :param data: Full data set with unavailable samples replaced by nan.
+    :type data: pandas.Series
+    :returns: Classification and trend values
+    :rtype: 2-tuple, list of strings and list of floats
+    """
+    bare_data = [sample for _, sample in data.iteritems()
+                 if not np.isnan(sample)]
+    # TODO: Put analogous iterator into jumpavg library.
+    groups = BitCountingClassifier.classify(bare_data)
+    groups.reverse()  # Just to use .pop() for FIFO.
+    classification = []
+    avgs = []
+    active_group = None
+    values_left = 0
+    avg = 0.0
+    for _, sample in data.iteritems():
+        if np.isnan(sample):
+            classification.append("outlier")
+            avgs.append(sample)
+            continue
+        if values_left < 1 or active_group is None:
+            values_left = 0
+            while values_left < 1:  # To ignore empty groups.
+                active_group = groups.pop()
+                values_left = len(active_group.values)
+            avg = active_group.metadata.avg
+            classification.append(active_group.metadata.classification)
+            avgs.append(avg)
+            values_left -= 1
+            continue
+        classification.append("normal")
+        avgs.append(avg)
+        values_left -= 1
+    return classification, avgs
+
+
+class Worker(multiprocessing.Process):
+    """Worker class used to process tasks in separate parallel processes.
+    """
+
+    def __init__(self, work_queue, data_queue, func):
+        """Initialization.
+
+        :param work_queue: Queue with items to process.
+        :param data_queue: Shared memory between processes. Queue which keeps
+            the result data. This data is then read by the main process and used
+            in further processing.
+        :param func: Function which is executed by the worker.
+        :type work_queue: multiprocessing.JoinableQueue
+        :type data_queue: multiprocessing.Manager().Queue()
+        :type func: Callable object
+        """
+        super(Worker, self).__init__()
+        self._work_queue = work_queue
+        self._data_queue = data_queue
+        self._func = func
+
+    def run(self):
+        """Method representing the process's activity.
+        """
+
+        while True:
+            try:
+                self.process(self._work_queue.get())
+            finally:
+                self._work_queue.task_done()
+
+    def process(self, item_to_process):
+        """Method executed by the runner.
+
+        :param item_to_process: Data to be processed by the function.
+        :type item_to_process: tuple
+        """
+        self._func(self.pid, self._data_queue, *item_to_process)
index 954f17d..15a144d 100755 (executable)
@@ -2,6 +2,8 @@
 
 set -x
 
+( cd new ; ./run_cpta.sh )
+
 # set default values in config array
 typeset -A DIR
 
@@ -30,5 +32,7 @@ STATUS=$(python pal.py \
     --force)
 RETURN_STATUS=$?
 
+mv new/_build _build/new
+
 echo ${STATUS}
 exit ${RETURN_STATUS}