8a3f8334a7f6be51138a6c7fb59cc2e60ad8f278
[csit.git] / resources / tools / presentation / generator_report.py
1 # Copyright (c) 2017 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Report generation.
15 """
16
17 import subprocess
18 import logging
19 import datetime
20
21 from os import makedirs, environ
22 from os.path import isdir
23 from shutil import copy, Error, make_archive
24
25 from utils import get_files
26 from errors import PresentationError
27
28
29 # .css file for the html format of the report
30 THEME_OVERRIDES = """/* override table width restrictions */
31 @media screen and (min-width: 767px) {
32     .wy-table-responsive table td, .wy-table-responsive table th {
33         white-space: normal !important;
34     }
35
36     .wy-table-responsive {
37         font-size: small;
38         margin-bottom: 24px;
39         max-width: 100%;
40         overflow: visible !important;
41     }
42 }
43 .rst-content blockquote {
44     margin-left: 0px;
45     line-height: 18px;
46     margin-bottom: 0px;
47 }
48 """
49
50 # Command to build the html format of the report
51 HTML_BUILDER = 'sphinx-build -v -c . -a ' \
52                '-b html -E ' \
53                '-D release={release} ' \
54                '-D version="{release} report - {date}" ' \
55                '{working_dir} ' \
56                '{build_dir}/'
57
58 # Command to build the pdf format of the report
59 PDF_BUILDER = 'sphinx-build -v -c . -a ' \
60               '-b latex -E ' \
61               '-D release={release} ' \
62               '-D version="{release} report - {date}" ' \
63               '{working_dir} ' \
64               '{build_dir}'
65
66
67 def generate_report(release, spec):
68     """Generate all formats and versions of the report.
69
70     :param release: Release string of the product.
71     :param spec: Specification read from the specification file.
72     :type release: str
73     :type spec: Specification
74     """
75
76     logging.info("Generating the report ...")
77
78     report = {
79         "html": generate_html_report,
80         "pdf": generate_pdf_report
81     }
82
83     for report_format, versions in spec.output.items():
84         report[report_format](release, spec, versions)
85
86     archive_input_data(spec)
87     archive_report(spec)
88
89     logging.info("Done.")
90
91
92 def generate_html_report(release, spec, versions):
93     """Generate html format of the report.
94
95     :param release: Release string of the product.
96     :param spec: Specification read from the specification file.
97     :param versions: List of versions to generate.
98     :type release: str
99     :type spec: Specification
100     :type versions: list
101     """
102
103     logging.info("  Generating the html report, give me a few minutes, please "
104                  "...")
105
106     cmd = HTML_BUILDER.format(
107         release=release,
108         date=datetime.date.today().strftime('%d-%b-%Y'),
109         working_dir=spec.environment["paths"]["DIR[WORKING,SRC]"],
110         build_dir=spec.environment["paths"]["DIR[BUILD,HTML]"])
111     _execute_command(cmd)
112
113     with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE]"], "w") as \
114             css_file:
115         css_file.write(THEME_OVERRIDES)
116
117     with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE2]"], "w") as \
118             css_file:
119         css_file.write(THEME_OVERRIDES)
120
121     logging.info("  Done.")
122
123
124 def generate_pdf_report(release, spec, versions):
125     """Generate html format of the report.
126
127     :param release: Release string of the product.
128     :param spec: Specification read from the specification file.
129     :param versions: List of versions to generate. Not implemented yet.
130     :type release: str
131     :type spec: Specification
132     :type versions: list
133     """
134
135     logging.info("  Generating the pdf report, give me a few minutes, please "
136                  "...")
137
138     convert_plots = "xvfb-run -a wkhtmltopdf {html} {pdf}.pdf"
139
140     # Convert PyPLOT graphs in HTML format to PDF.
141     plots  = get_files(spec.environment["paths"]["DIR[STATIC,VPP]"], "html")
142     for plot in plots:
143         file_name = "{0}".format(plot.rsplit(".", 1)[0])
144         cmd = convert_plots.format(html=plot, pdf=file_name)
145         _execute_command(cmd)
146
147     # Generate the LaTeX documentation
148     build_dir = spec.environment["paths"]["DIR[BUILD,LATEX]"]
149     cmd = PDF_BUILDER.format(
150         release=release,
151         date=datetime.date.today().strftime('%d-%b-%Y'),
152         working_dir=spec.environment["paths"]["DIR[WORKING,SRC]"],
153         build_dir=build_dir)
154     _execute_command(cmd)
155
156     # Build pdf documentation
157     archive_dir = spec.environment["paths"]["DIR[STATIC,ARCH]"]
158     cmds = [
159         'cd {build_dir} && '
160         'pdflatex -shell-escape -interaction nonstopmode csit.tex || true'.
161         format(build_dir=build_dir),
162         'cd {build_dir} && '
163         'pdflatex -interaction nonstopmode csit.tex || true'.
164         format(build_dir=build_dir),
165         'cd {build_dir} && '
166         'cp csit.pdf ../{archive_dir}/csit_{release}.pdf'.
167         format(build_dir=build_dir,
168                archive_dir=archive_dir,
169                release=release)
170     ]
171
172     for cmd in cmds:
173         _execute_command(cmd)
174
175     logging.info("  Done.")
176
177
178 def archive_report(spec):
179     """Archive the report.
180
181     :param spec: Specification read from the specification file.
182     :type spec: Specification
183     """
184
185     logging.info("  Archiving the report ...")
186
187     make_archive("csit.report",
188                  "gztar",
189                  base_dir=spec.environment["paths"]["DIR[BUILD,HTML]"])
190
191     logging.info("  Done.")
192
193
194 def archive_input_data(spec):
195     """Archive the report.
196
197     :param spec: Specification read from the specification file.
198     :type spec: Specification
199     :raises PresentationError: If it is not possible to archive the input data.
200     """
201
202     logging.info("    Archiving the input data files ...")
203
204     if spec.is_debug:
205         extension = spec.debug["input-format"]
206     else:
207         extension = spec.input["file-format"]
208     data_files = get_files(spec.environment["paths"]["DIR[WORKING,DATA]"],
209                            extension=extension)
210     dst = spec.environment["paths"]["DIR[STATIC,ARCH]"]
211     logging.info("      Destination: {0}".format(dst))
212
213     try:
214         if not isdir(dst):
215             makedirs(dst)
216
217         for data_file in data_files:
218             logging.info("      Copying the file: {0} ...".format(data_file))
219             copy(data_file, dst)
220
221     except (Error, OSError) as err:
222         raise PresentationError("Not possible to archive the input data.",
223                                 str(err))
224
225     logging.info("    Done.")
226
227
228 def _execute_command(cmd):
229     """Execute the command in a subprocess and log the stdout and stderr.
230
231     :param cmd: Command to execute.
232     :type cmd: str
233     :returns: Return code of the executed command.
234     :rtype: int
235     """
236
237     env = environ.copy()
238     proc = subprocess.Popen(
239         [cmd],
240         stdout=subprocess.PIPE,
241         stderr=subprocess.PIPE,
242         shell=True,
243         env=env)
244
245     stdout, stderr = proc.communicate()
246
247     logging.info(stdout)
248     logging.info(stderr)
249
250     if proc.returncode != 0:
251         logging.error("    Command execution failed.")
252     return proc.returncode