izzi
SVG SUBSET C++ API
Loading...
Searching...
No Matches
izzi-tables.h
Go to the documentation of this file.
1// izzi HTML tables -*- mode: C++ -*-
2
3// Copyright (c) 2025-2026, Benjamin De Kosnik <b.dekosnik@gmail.com>
4
5// This file is part of the alpha60 library. This library is free
6// software; you can redistribute it and/or modify it under the terms
7// of the GNU General Public License as published by the Free Software
8// Foundation; either version 3, or (at your option) any later
9// version.
10
11// This library is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15
16#ifndef izzi_TABLES_H
17#define izzi_TABLES_H 1
18
19#include "a60-svg.h"
20#include <string_view>
21
22namespace svg {
23
24using std::ostringstream;
25
26/// Arithmetic on date stamps and date hour stamps.
27/// Return time_point associated with that iso-date string and hour.
28std::chrono::year_month_day
29date_stamp_to_year_month_day(const string dstamp)
30{
31 string ddate = dstamp.substr(0, 10);
32
33#ifndef __clang__
34 std::chrono::year_month_day ymd;
35 std::istringstream iss(ddate);
36 iss >> std::chrono::parse("%F", ymd);
37 if (iss.fail())
38 {
39 string m("date_stamp_to_year_month_day: failed to parse (");
40 m += ddate;
41 m += ")";
42 m += k::newline;
43 throw std::runtime_error(m);
44 }
45#else
46 // emscripten
47 // Extract parts as integers
48 int y = std::stoi(ddate.substr(0, 4));
49 int m = std::stoi(ddate.substr(5, 2));
50 int d = std::stoi(ddate.substr(8, 2));
51
52 // Construct the object directly
53 std::chrono::year_month_day ymd {
54 std::chrono::year{y},
55 std::chrono::month(static_cast<unsigned int>(m)),
56 std::chrono::day(static_cast<unsigned int>(d))
57 };
58#endif
59 return ymd;
60}
61
62
63/// Converts ISO datestamp to HTML time element with accessible-readable
64/// long form with month names, instead of just a list of digits.
65string
67{
68 std::chrono::year_month_day ymd = date_stamp_to_year_month_day(ds);
69 string ds_long = std::format("{:%B %d, %Y}", ymd);
70
71 // Build the HTML time element
72 string html_element;
73
74 // <time datetime="YYYY-MM-DD" aria-label="Month Day, Year">Month Day, Year</time>
75 html_element = "<time datetime=";
76 html_element += k::quote;
77 html_element += ds;
78 html_element += k::quote;
79 html_element += k::space;
80 html_element += "aria-label=";
81 html_element += k::quote;
82 html_element += ds_long;
83 html_element += k::quote;
84 html_element += ">";
85 html_element += k::space;
86 html_element += ds;
87 html_element += k::space;
88 html_element += "</time>";
89
90 return html_element;
91}
92
93
94/// Wrap table in HTML details element, containing an H3 media object
95/// @param tbl is the tbody element from an HTML table element
96/// @param htitle is the H3 title element to use for the summary title
97string
98detail_summary_table(const string& tbl, const string& htitle)
99{
100 std::ostringstream oss;
101 if (oss.good())
102 {
103 constexpr const char* dtlstart = R"_delimiter_(
104<details>
105 <summary>
106 <h3>{}</h3>
107 </summary>
108 <div class="table-container">
109 )_delimiter_";
110 const string dtlend = R"_delimiter_(</div></details>)_delimiter_";
111
112 oss << std::format(dtlstart, htitle) << k::newline;
113 oss << tbl;
114 oss << dtlend << k::newline;
115 }
116 return oss.str();
117}
118
120/// Cleanup Pandas HTML table export.
121/// Pandas html table export from properly constructed json is excellent.
122/// However, knowing the data, it can be made more legible with a smarter table head.
123/// So, strip thead and simplify names, make multi-column, multi-row for clarity.
124string
125simplify_pandas_table(const string minimetricf)
126{
127 string html;
128 std::ifstream ifs(minimetricf);
129 if (ifs.good())
130 {
131 /*
132 <thead>
133 <tr style="text-align: right;">
134 <th>metric</th>
135 <th>firefox_median</th>
136 <th>firefox_rsd</th>
137 <th>chrome_median</th>
138 <th>chrome_rsd</th>
139 <th>difference</th>
140 </tr>
141 </thead>
142 */
143 ostringstream oss;
144 oss << ifs.rdbuf();
145 html = oss.str();
146
147 const string thead_start("<thead>");
148 const string thead_end("</thead>");
149 auto startpos = html.find(thead_start);
150 auto endpos = html.find(thead_end);
151
152 const string theadnu = R"_delimiter_(
153 <thead>
154 <tr>
155 <th scope="col" rowspan="2" width="25%">metric</th>
156 <th scope="col" colspan="2" width="25%">firefox</th>
157 <th scope="col" colspan="2" width="25%">chrome</th>
158 <th scope="col" rowspan="2" width="25%">difference</th>
159 </tr>
160 <tr>
161 <th scope="col">median</th>
162 <th scope="col">rsd</th>
163 <th scope="col">median</th>
164 <th scope="col">rsd</th>
165 </tr>
166 </thead>
167 )_delimiter_";
168
169 if (startpos != string::npos && endpos != string::npos)
170 {
171 auto length = endpos + thead_end.size() - startpos;
172 html.replace(startpos, length, theadnu);
173 }
174 else
175 std::cout << "cannot find table heads to swap" << std::endl;
176 }
177 return html;
178}
179
180
181/// Link row in table.
182string
183serialize_link_row_2c_4f(string_view uri1, string_view link1,
184 string_view uri2, string_view link2)
185{
186 // td elements == left
187 // th elements == center
188
189 // NB: Use {{}} for literal quoting, as src may have '/' and '&' etc.
190 constexpr const char* tdblank = R"_delimiter_(<th style="padding: 5px;"><a href="{}"> {} </a></th>)_delimiter_";
191
192 ostringstream oss;
193 oss << "<tr>" << svg::k::newline;
194 oss << std::format(tdblank, uri1, link1) << svg::k::newline;
195 oss << std::format(tdblank, uri2, link2) << svg::k::newline;
196 oss << "</tr>" << svg::k::newline;
197 return oss.str();
198}
199
200
201/// Image row in table.
202string
203serialize_image_row_2c_4f(string_view img1src, string_view img1alt,
204 string_view img2src, string_view img2alt)
205{
206 // td elements == left
207 // th elements == center
208
209 // NB: Use {{}} for literal quoting, as src may have '/' and '&' etc.
210 constexpr const char* tdblank = R"_delimiter_(<th style="padding: 5px;"><img src="{{}}" alt="{{}}" width="50%"></th>)_delimiter_";
211
212 ostringstream oss;
213 oss << "<tr>" << svg::k::newline;
214 oss << std::format(tdblank, img1src, img1alt) << svg::k::newline;
215 oss << std::format(tdblank, img2src, img2alt) << svg::k::newline;
216 oss << "</tr>" << svg::k::newline;
217 return oss.str();
218}
219
220
221/// Serialize borderless image table.
222void
223serialize_2_image_table(const string& gtitlelc)
225 const string tblstart = R"_delimiter_(<table style="border-collapse: collapse; width: 100%;">)_delimiter_";
226 const string tblend = "</table>";
227
228 string img1 = "";
229 string img1alt = "";
230 string img2 = "";
231 string img2alt = "";
233 ostringstream oss;
234 oss << tblstart << svg::k::newline;
235 oss << serialize_image_row_2c_4f(img1, img1alt, img2, img2alt);
236 oss << tblend << svg::k::newline;
237
238 const string metricf = gtitlelc + "-image-table.html";
239 std::ofstream ofs(metricf);
240 if (ofs.good())
241 ofs << oss.str() << svg::k::newline;
242}
243
244/// Caption element, both without styling and as H3 element in markdown.
245const string cap = "<caption>";
246const string capH3 = R"_delimiter_(<caption style="font-size: 1.17em; font-weight: bold; margin: 1em 0; text-align: left;">)_delimiter_";
247
248
249
250/// Serialize information about the analysis passes as an html table.
251/// Flatten all objects into one total number.
252void
253serialize_meta_collection_table(const string& gtitlelc, const string sdur,
254 const uint btihasz,
255 const uint dl, const uint ul)
256{
257 /*
258 HTML table element, simplified as
259 <table>
260 <thead>
261 </thead>
262 <tbody>
263 <tr>
264 <td>HTML tables</td>
265 </tr>
266 </tbody>
267 </table>
268
269 duration btihasz downloaders* uploaders*
270 * = total, per btiha
271
272 QED 4-column-6-field row
273 */
274 const string metricf = gtitlelc + "-meta-collection-table.html";
275 std::ofstream ofs(metricf);
276 if (ofs.good())
277 {
278 const string thead = R"_delimiter_(
279 <thead>
280 <tr>
281 <th scope="col" rowspan="2" width="25%">duration</th>
282 <th scope="col" rowspan="2" width="25%">btiha size</th>
283 <th scope="col" colspan="2" width="25%">downloaders</th>
284 <th scope="col" colspan="2" width="25%">uploaders</th>
285 </tr>
286 <tr>
287 <th scope="col">total</th>
288 <th scope="col">per btiha</th>
289 <th scope="col">total</th>
290 <th scope="col">per btiha</th>
291 </tr>
292 </thead>
293 )_delimiter_";
294
295 ofs.imbue(std::locale(""));
296 ofs << std::fixed << std::setprecision(0);
297
298 ofs << "<table>" << svg::k::newline;
299 ofs << thead << svg::k::newline;
300
301#if 0
302 string tts(gtitlelc);
303 std::ranges::replace(tts, k::hyphen, k::space);
304 ofs << cap << tts << "</caption>" << svg::k::newline;
305#else
306 ofs << capH3 << "Aggregate Table" << "</caption>" << svg::k::newline;
307#endif
308
309 ofs << "<tbody>" << svg::k::newline;
310 ofs << "<tr>" << svg::k::newline;
311
312 ofs << "<td>" << sdur << "</td>" << svg::k::newline;
313 ofs << "<th>" << btihasz << "</th>" << svg::k::newline;
314 ofs << "<td>" << dl << "</td>" << svg::k::newline;
315 ofs << "<td>" << dl / btihasz << "</td>" << svg::k::newline;
316 ofs << "<td>" << ul << "</td>" << svg::k::newline;
317 ofs << "<td>" << ul / btihasz << "</td>" << svg::k::newline;
318
319 ofs << "</tr>" << svg::k::newline;
320 ofs << "</tbody>" << svg::k::newline;
321 ofs << "</table>" << svg::k::newline;
322 }
323}
324
325
326/// Serialize row of media object information.
327///
328/// media_object btihasz downloaders* uploaders* #weeks sample_dates
329/// * = total, per btiha
330string
331serialize_row_6c_8f(const string moname, const uint btihasz,
332 const uint dl, const uint ul,
333 const uint weeksn, const string sdates)
334{
335 ostringstream oss;
336 oss.imbue(std::locale(""));
337 oss << std::fixed << std::setprecision(0);
338
339 oss << "<tr>" << svg::k::newline;
340
341 // td elements == left
342 // th elements == center
343 oss << "<td>" << moname << "</td>" << svg::k::newline;
344 oss << "<td>" << weeksn << "</td>" << svg::k::newline;
345 oss << "<td>" << sdates << "</td>" << svg::k::newline;
346 oss << "<td>" << btihasz << "</td>" << svg::k::newline;
347 oss << "<td>" << dl << "</td>" << svg::k::newline;
348 oss << "<td>" << dl / btihasz << "</td>" << svg::k::newline;
349 oss << "<td>" << ul << "</td>" << svg::k::newline;
350 oss << "<td>" << ul / btihasz << "</td>" << svg::k::newline;
351
352 oss << "</tr>" << svg::k::newline;
353
354 return oss.str();
355}
356
357
358} // namespace svg
359
360#endif
constexpr char newline('\n')
std::chrono::year_month_day date_stamp_to_year_month_day(const string dstamp)
Arithmetic on date stamps and date hour stamps. Return time_point associated with that iso-date strin...
Definition izzi-tables.h:29
string serialize_image_row_2c_4f(string_view img1src, string_view img1alt, string_view img2src, string_view img2alt)
Image row in table.
string serialize_row_6c_8f(const string moname, const uint btihasz, const uint dl, const uint ul, const uint weeksn, const string sdates)
Serialize row of media object information.
string serialize_link_row_2c_4f(string_view uri1, string_view link1, string_view uri2, string_view link2)
Link row in table.
void serialize_2_image_table(const string &gtitlelc)
Serialize borderless image table.
const string cap
Caption element, both without styling and as H3 element in markdown.
string iso_datestamp_string_to_html_time(const string ds)
Converts ISO datestamp to HTML time element with accessible-readable long form with month names,...
Definition izzi-tables.h:66
const string capH3
string simplify_pandas_table(const string minimetricf)
Cleanup Pandas HTML table export. Pandas html table export from properly constructed json is excellen...
unsigned int uint
Definition a60-svg.h:58
void serialize_meta_collection_table(const string &gtitlelc, const string sdur, const uint btihasz, const uint dl, const uint ul)
Serialize information about the analysis passes as an html table. Flatten all objects into one total ...
string detail_summary_table(const string &tbl, const string &htitle)
Wrap table in HTML details element, containing an H3 media object.
Definition izzi-tables.h:98