CSIT-836: PDF format: Improvements in pdf layout
[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                '-t html ' \
54                '-D release={release} ' \
55                '-D version="{release} report - {date}" ' \
56                '{working_dir} ' \
57                '{build_dir}/'
58
59 # Command to build the pdf format of the report
60 PDF_BUILDER = 'sphinx-build -v -c . -a ' \
61               '-b latex -E ' \
62               '-t latex ' \
63               '-D release={release} ' \
64               '-D version="{release} report - {date}" ' \
65               '{working_dir} ' \
66               '{build_dir}'
67
68
69 def generate_report(release, spec):
70     """Generate all formats and versions of the report.
71
72     :param release: Release string of the product.
73     :param spec: Specification read from the specification file.
74     :type release: str
75     :type spec: Specification
76     """
77
78     logging.info("Generating the report ...")
79
80     report = {
81         "html": generate_html_report,
82         "pdf": generate_pdf_report
83     }
84
85     for report_format, versions in spec.output.items():
86         report[report_format](release, spec, versions)
87
88     archive_input_data(spec)
89     archive_report(spec)
90
91     logging.info("Done.")
92
93
94 def generate_html_report(release, spec, versions):
95     """Generate html format of the report.
96
97     :param release: Release string of the product.
98     :param spec: Specification read from the specification file.
99     :param versions: List of versions to generate.
100     :type release: str
101     :type spec: Specification
102     :type versions: list
103     """
104
105     logging.info("  Generating the html report, give me a few minutes, please "
106                  "...")
107
108     cmd = HTML_BUILDER.format(
109         release=release,
110         date=datetime.date.today().strftime('%d-%b-%Y'),
111         working_dir=spec.environment["paths"]["DIR[WORKING,SRC]"],
112         build_dir=spec.environment["paths"]["DIR[BUILD,HTML]"])
113     _execute_command(cmd)
114
115     with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE]"], "w") as \
116             css_file:
117         css_file.write(THEME_OVERRIDES)
118
119     with open(spec.environment["paths"]["DIR[CSS_PATCH_FILE2]"], "w") as \
120             css_file:
121         css_file.write(THEME_OVERRIDES)
122
123     logging.info("  Done.")
124
125
126 def generate_pdf_report(release, spec, versions):
127     """Generate html format of the report.
128
129     :param release: Release string of the product.
130     :param spec: Specification read from the specification file.
131     :param versions: List of versions to generate. Not implemented yet.
132     :type release: str
133     :type spec: Specification
134     :type versions: list
135     """
136
137     logging.info("  Generating the pdf report, give me a few minutes, please "
138                  "...")
139
140     convert_plots = "xvfb-run -a wkhtmltopdf {html} {pdf}.pdf"
141
142     # Convert PyPLOT graphs in HTML format to PDF.
143     plots  = get_files(spec.environment["paths"]["DIR[STATIC,VPP]"], "html")
144     for plot in plots:
145         file_name = "{0}".format(plot.rsplit(".", 1)[0])
146         cmd = convert_plots.format(html=plot, pdf=file_name)
147         _execute_command(cmd)
148
149     # Generate the LaTeX documentation
150     build_dir = spec.environment["paths"]["DIR[BUILD,LATEX]"]
151     cmd = PDF_BUILDER.format(
152         release=release,
153         date=datetime.date.today().strftime('%d-%b-%Y'),
154         working_dir=spec.environment["paths"]["DIR[WORKING,SRC]"],
155         build_dir=build_dir)
156     _execute_command(cmd)
157
158     # Build pdf documentation
159     archive_dir = spec.environment["paths"]["DIR[STATIC,ARCH]"]
160     cmds = [
161         'cd {build_dir} && '
162         'pdflatex -shell-escape -interaction nonstopmode csit.tex || true'.
163         format(build_dir=build_dir),
164         'cd {build_dir} && '
165         'pdflatex -interaction nonstopmode csit.tex || true'.
166         format(build_dir=build_dir),
167         'cd {build_dir} && '
168         'cp csit.pdf ../{archive_dir}/csit_{release}.pdf'.
169         format(build_dir=build_dir,
170                archive_dir=archive_dir,
171                release=release)
172     ]
173
174     for cmd in cmds:
175         _execute_command(cmd)
176
177     logging.info("  Done.")
178
179
180 def archive_report(spec):
181     """Archive the report.
182
183     :param spec: Specification read from the specification file.
184     :type spec: Specification
185     """
186
187     logging.info("  Archiving the report ...")
188
189     make_archive("csit.report",
190                  "gztar",
191                  base_dir=spec.environment["paths"]["DIR[BUILD,HTML]"])
192
193     logging.info("  Done.")
194
195
196 def archive_input_data(spec):
197     """Archive the report.
198
199     :param spec: Specification read from the specification file.
200     :type spec: Specification
201     :raises PresentationError: If it is not possible to archive the input data.
202     """
203
204     logging.info("    Archiving the input data files ...")
205
206     if spec.is_debug:
207         extension = spec.debug["input-format"]
208     else:
209         extension = spec.input["file-format"]
210     data_files = get_files(spec.environment["paths"]["DIR[WORKING,DATA]"],
211                            extension=extension)
212     dst = spec.environment["paths"]["DIR[STATIC,ARCH]"]
213     logging.info("      Destination: {0}".format(dst))
214
215     try:
216         if not isdir(dst):
217             makedirs(dst)
218
219         for data_file in data_files:
220             logging.info("      Copying the file: {0} ...".format(data_file))
221             copy(data_file, dst)
222
223     except (Error, OSError) as err:
224         raise PresentationError("Not possible to archive the input data.",
225                                 str(err))
226
227     logging.info("    Done.")
228
229
230 def _execute_command(cmd):
231     """Execute the command in a subprocess and log the stdout and stderr.
232
233     :param cmd: Command to execute.
234     :type cmd: str
235     :returns: Return code of the executed command.
236     :rtype: int
237     """
238
239     env = environ.copy()
240     proc = subprocess.Popen(
241         [cmd],
242         stdout=subprocess.PIPE,
243         stderr=subprocess.PIPE,
244         shell=True,
245         env=env)
246
247     stdout, stderr = proc.communicate()
248
249     logging.info(stdout)
250     logging.info(stderr)
251
252     if proc.returncode != 0:
253         logging.error("    Command execution failed.")
254     return proc.returncode