clarified explicit Build_Status.Data operations;
authorwenzelm
Sun, 07 May 2017 16:14:49 +0200
changeset 65754 05c6a29c9132
parent 65753 787e5ee6ef53
child 65755 21b4bfa6d204
clarified explicit Build_Status.Data operations; more HTML output;
src/Pure/Admin/build_status.scala
--- a/src/Pure/Admin/build_status.scala	Sun May 07 16:04:19 2017 +0200
+++ b/src/Pure/Admin/build_status.scala	Sun May 07 16:14:49 2017 +0200
@@ -44,7 +44,12 @@
       !timing.is_zero && timing.elapsed >= elapsed_threshold
   }
 
-  type Data = Map[String, Map[String, List[Entry]]]
+  sealed case class Data(date: Date, entries: Map[String, Map[String, List[Entry]]])
+  {
+    def sorted_entries: List[(String, List[(String, List[Entry])])] =
+      entries.toList.sortBy(_._1).map({ case (name, session_entries) =>
+        (name, session_entries.toList.sortBy(_._2.head.timing.elapsed.ms).reverse) })
+  }
 
 
   /* read data */
@@ -57,7 +62,8 @@
     elapsed_threshold: Time = Time.zero,
     verbose: Boolean = false): Data =
   {
-    var data: Data = Map.empty
+    val date = Date.now()
+    var data_entries = Map.empty[String, Map[String, List[Entry]]]
 
     val store = Build_Log.store(options)
     using(store.open_database())(db =>
@@ -113,51 +119,43 @@
                   Build_Log.Data.ml_timing_cpu,
                   Build_Log.Data.ml_timing_gc))
 
-            val session_entries = data.getOrElse(name, Map.empty)
+            val session_entries = data_entries.getOrElse(name, Map.empty)
             val entries = session_entries.getOrElse(session, Nil)
-            data += (name -> (session_entries + (session -> (entry :: entries))))
+            data_entries += (name -> (session_entries + (session -> (entry :: entries))))
           }
         })
       }
     })
 
-    for {
-      (name, session_entries) <- data
-      session_entries1 <-
-        {
-          val session_entries1 =
-            for {
-              (session, entries) <- session_entries
-              if entries.filter(_.check(elapsed_threshold)).length >= 3
-            } yield (session, entries)
-          if (session_entries1.isEmpty) None
-          else Some(session_entries1)
-        }
-    } yield (name, session_entries1)
+    Data(date,
+      for {
+        (name, session_entries) <- data_entries
+        session_entries1 <-
+          {
+            val session_entries1 =
+              for {
+                (session, entries) <- session_entries
+                if entries.filter(_.check(elapsed_threshold)).length >= 3
+              } yield (session, entries)
+            if (session_entries1.isEmpty) None
+            else Some(session_entries1)
+          }
+      } yield (name, session_entries1))
   }
 
 
   /* present data */
 
-  private val html_header = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head><title>Build status</title></head>
-<body>
-"""
-  private val html_footer = """
-</body>
-</html>
-"""
-
   def present_data(data: Data,
     progress: Progress = No_Progress,
     target_dir: Path = default_target_dir,
     image_size: (Int, Int) = default_image_size,
     ml_timing: Option[Boolean] = None)
   {
-    val data_entries = data.toList.sortBy(_._1)
-    for ((name, session_entries) <- data_entries) {
-      val dir = target_dir + Path.explode(name)
+    val data_entries = data.sorted_entries
+
+    for ((data_name, session_entries) <- data_entries) {
+      val dir = target_dir + Path.explode(data_name)
       progress.echo("output " + dir)
       Isabelle_System.mkdirs(dir)
 
@@ -209,26 +207,37 @@
             val result =
               Isabelle_System.bash("\"$ISABELLE_GNUPLOT\" " + File.bash_path(gnuplot_file))
             if (result.rc != 0)
-              result.error("Gnuplot failed for " + name + "/" + session).check
+              result.error("Gnuplot failed for " + data_name + "/" + session).check
           }
         }
       }
 
+      val sessions = session_entries.toList.sortBy(_._2.head.timing.elapsed.ms).reverse
+      val heading = "Build status for " + data_name + " (" + data.date + ")"
+
       File.write(dir + Path.basic("index.html"),
-        html_header + "\n<h1>" + HTML.output(name) + "</h1>\n" +
-        cat_lines(
-          session_entries.toList.map(_._1).sorted.map(session =>
-            """<br/><img src=""" + quote(HTML.output(session + ".png")) + """><br/>""")) +
-        "\n" + html_footer)
+        HTML.output_document(
+          List(HTML.title(heading)),
+          HTML.chapter(heading) ::
+          HTML.itemize(
+            sessions.map({ case (name, entries) =>
+              HTML.link("#session_" + name, HTML.text(name)) ::
+              HTML.text(" (" + entries.head.timing.message_resources + ")") })) ::
+          sessions.flatMap({ case (name, entries) =>
+            List(
+              HTML.section(name + " (" + entries.head.timing.message_resources + ")") +
+                HTML.id("session_" + name),
+              HTML.par(List(HTML.image(name + ".png")))) })))
     }
 
+    val heading = "Build status (" + data.date + ")"
+
     File.write(target_dir + Path.basic("index.html"),
-      html_header + "\n<ul>\n" +
-      cat_lines(
-        data_entries.map(_._1).map(name =>
-          """<li> <a href=""" + quote(HTML.output(name + "/index.html")) + """>""" +
-            HTML.output(name) + """</a> </li>""")) +
-      "\n</ul>\n" + html_footer)
+      HTML.output_document(
+        List(HTML.title(heading)),
+        List(HTML.chapter(heading),
+          HTML.itemize(data_entries.map({ case (name, _) =>
+            List(HTML.link(name + "/index.html", HTML.text(name))) })))))
   }