1 /**
2 Copyright: Copyright (c) 2021, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module dextool.plugin.mutate.backend.report.html.page_worklist;
11 
12 import logger = std.experimental.logger;
13 import std.array : empty;
14 import std.conv : to;
15 import std.datetime : Clock;
16 import std.exception : collectException;
17 import std.format : format;
18 import std.traits : EnumMembers;
19 import std.typecons : tuple;
20 
21 import arsd.dom : Document, Element, Table, RawSource;
22 import miniorm : spinSql;
23 import my.path : AbsolutePath, Path;
24 import my.optional;
25 
26 import dextool.cachetools;
27 import dextool.plugin.mutate.backend.database : Database, MutantInfo2;
28 import dextool.plugin.mutate.backend.report.html.constants : HtmlStyle = Html;
29 import dextool.plugin.mutate.backend.report.html.constants;
30 import dextool.plugin.mutate.backend.report.html.tmpl : tmplBasicPage,
31     dashboardCss, tmplSortableTable;
32 import dextool.plugin.mutate.backend.report.html.utility : pathToHtmlLink, toShortDate, toShortTime;
33 import dextool.plugin.mutate.backend.resource;
34 import dextool.plugin.mutate.backend.type : Mutation;
35 import dextool.plugin.mutate.backend.report.html.utility : generatePopupHelp;
36 
37 @safe:
38 
39 void makeWorklistPage(ref Database db, Element root, const AbsolutePath mutantPageFname) @trusted {
40     makePage(db, mutantPageFname);
41 }
42 
43 private:
44 
45 void makePage(ref Database db, const AbsolutePath pageFname) @system {
46     import std.algorithm : map, filter;
47     import std.path : buildPath;
48     import std.range : enumerate;
49     import dextool.plugin.mutate.backend.database : IterateMutantRow2, MutationStatusId;
50     import dextool.plugin.mutate.backend.report.analyzers : calcAvgPerMutant;
51 
52     auto doc = tmplBasicPage.dashboardCss;
53     scope (success)
54         () {
55         import std.stdio : File;
56 
57         auto fout = File(pageFname, "w");
58         fout.write(doc.toPrettyString);
59     }();
60 
61     doc.title(format("Worklist %s", Clock.currTime));
62     doc.mainBody.addChild("h1", "Worklist");
63     doc.mainBody.setAttribute("onload", "init()");
64 
65     {
66         auto data = dashboard();
67         auto style = doc.root.childElements("head")[0].addChild("style");
68         style.addChild(new RawSource(doc, data.bootstrapCss.get));
69         style.addChild(new RawSource(doc, data.dashboardCss.get));
70         style.addChild(new RawSource(doc, tmplDefaultCss));
71 
72         auto script = doc.root.childElements("head")[0].addChild("script");
73         script.addChild(new RawSource(doc, jsIndex));
74     }
75 
76     auto root = doc.mainBody;
77 
78     void addPopupHelp(Element e, string header) {
79         switch (header) {
80         case "Tested":
81             generatePopupHelp(e, "Date when the mutant was last tested/executed.");
82             break;
83         case "Finished":
84             generatePopupHelp(e, "Prediction for when the mutant is executed.");
85             break;
86         case "Priority":
87             generatePopupHelp(e,
88                     "How important it is to kill the mutant. It is based on modified source code size.");
89             break;
90         default:
91             break;
92         }
93     }
94 
95     auto tbl = tmplSortableTable(root, [
96             "Order", "ID", "Link", "Priority", "Tested", "Status", "Finished"
97             ], &addPopupHelp);
98 
99     static string toLinkPath(Path path, MutationStatusId id) {
100         return format!"%s#%s"(buildPath(HtmlStyle.fileDir, pathToHtmlLink(path)), id);
101     }
102 
103     const avg = calcAvgPerMutant(db);
104 
105     foreach (data; spinSql!(() => db.worklistApi.getAll).map!(
106             a => spinSql!(() => tuple(a.prio, db.mutantApi.getMutantInfo(a.id))))
107             .filter!(a => a[1].hasValue)
108             .map!(a => tuple(a[0], a[1].orElse(MutantInfo2.init)))
109             .enumerate) {
110         auto mut = data.value[1];
111         auto r = tbl.appendRow;
112 
113         r.addChild("td", data.index.to!string);
114         r.addChild("td", mut.id.get.to!string);
115         r.addChild("td").addChild("a", format("%s:%s", mut.file,
116                 mut.sloc.line)).href = toLinkPath(mut.file, mut.id);
117         r.addChild("td", data.value[0].get.to!string);
118         r.addChild("td", mut.status == Mutation.Status.unknown ? "" : mut.updated.toShortDate);
119         r.addChild("td", mut.status.to!string);
120         r.addChild("td", (data.index * avg.get).toShortTime);
121     }
122 }