# HG changeset patch
# User Raphaël Gomès <rgomes@octobus.net>
# Date 1657215875 -7200
#      Thu Jul 07 19:44:35 2022 +0200
# Node ID 200951f8524902bd69f4b0f3c3227ffd53fd277d
# Parent  2396316c0f7a786b4ff973405dd19bcc162ab4e5
web-ui: start merging graphs

diff --git a/web-ui/src/App.svelte b/web-ui/src/App.svelte
--- a/web-ui/src/App.svelte
+++ b/web-ui/src/App.svelte
@@ -7,12 +7,12 @@
 <main>
   <h1>Poulpe!</h1>
 
-  {#each Object.entries(results) as [name, branches]}
+  {#each Object.entries(results) as [name, variants]}
     <h2>{name}</h2>
-    {#each Object.entries(branches) as [branchName, variants]}
-    <h3>Branch: {branchName}</h3>
-      {#each Object.entries(variants) as [variant, data]}
-        <h3>Variant: {variant}</h3>
+    {#each Object.entries(variants) as [variantName, branches]}
+      <h3>Variant: {variantName}</h3>
+      {#each Object.entries(branches) as [branchName, data]}
+        <h3>Branch: {branchName}</h3>
         <Benchmark {data} />
       {/each}
     {/each}
diff --git a/web-ui/src/config.ts b/web-ui/src/config.ts
new file mode 100644
--- /dev/null
+++ b/web-ui/src/config.ts
@@ -0,0 +1,54 @@
+export const COLORS = [
+    "#FF6633",
+    "#FFB399",
+    "#FF33FF",
+    "#00B3E6",
+    "#1AB399",
+    "#E6B333",
+    "#3366E6",
+    "#999966",
+    "#B34D4D",
+    "#80B300",
+    "#809900",
+    "#6680B3",
+    "#FF1A66",
+    "#66991A",
+    "#FF99E6",
+    "#CCFF1A",
+    "#33FFCC",
+    "#66994D",
+    "#E6331A",
+    "#B366CC",
+    "#99FF99",
+    "#E6B3B3",
+    "#4D8000",
+    "#B33300",
+    "#CC80CC",
+    "#66664D",
+    "#991AFF",
+    "#E666FF",
+    "#4DB3FF",
+    "#E666B3",
+    "#33991A",
+    "#CC9999",
+    "#B3B31A",
+    "#00E680",
+    "#4D8066",
+    "#809980",
+    "#E6FF80",
+    "#1AFF33",
+    "#999933",
+    "#FF3380",
+    "#CCCC00",
+    "#66E64D",
+    "#4D80CC",
+    "#9900B3",
+    "#FFFF99",
+    "#E64D66",
+    "#4DB380",
+    "#FF4D4D",
+    "#99E6E6",
+    "#6666FF",
+  ];
+
+export const GRAPH_SIZE = 400;
\ No newline at end of file
diff --git a/web-ui/src/lib/Benchmark.svelte b/web-ui/src/lib/Benchmark.svelte
--- a/web-ui/src/lib/Benchmark.svelte
+++ b/web-ui/src/lib/Benchmark.svelte
@@ -6,9 +6,7 @@
 
 <div class="benchmark">
     <div class="graphs">
-        {#each Object.entries(data) as graph_data}
-            <Graph data={graph_data} />
-        {/each}
+        <Graph {data} />
     </div>
 </div>
 
@@ -16,6 +14,8 @@
     .graphs {
         display: grid;
         gap: 1.5rem;
-        grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
+        grid-template-columns: repeat(auto-fit, minmax(500px, max-content));
+        justify-content: center;
+        align-content: center;
     }
 </style>
diff --git a/web-ui/src/lib/Graph.svelte b/web-ui/src/lib/Graph.svelte
--- a/web-ui/src/lib/Graph.svelte
+++ b/web-ui/src/lib/Graph.svelte
@@ -1,53 +1,52 @@
 <script lang="ts">
   import Chart from "chart.js/auto";
+  import nodemap from "../../../mercurial-mapping.json";
   import { onMount } from "svelte";
-  export let data = [
-    "mozilla-dirstate-v1",
-    [
-      {
-        "bin-env-vars": {
-          hg: {
-            version: "4.8.2+1-7354292899eb",
-            changeset: {
-              node: "7354292899ebc84f9b0bb840ad4cad14545bc798",
-            },
-          },
-        },
-        result: {
-          time: {
-            median: 3.69134702416,
-          },
-        },
-      },
-    ],
-  ];
+  import { COLORS } from "../config.js";
+  import { getNodeForResult, sortVersions } from "../utils";
+
+  export let data;
   let myChart;
 
   onMount(() => {
     const ctx = myChart.getContext("2d");
 
     const datasets = [];
+    const labels = new Set();
 
-    const [repoName, results] = data;
-    let graphData = [];
-    for (let result of results) {
-      graphData.push({
-        x: result["bin-env-vars"]["hg"]["version"].split("+")[0],
-        y: result["result"]["time"]["median"],
-        node: result["bin-env-vars"]["hg"]["changeset"]["node"],
+    for (const [index, [repoVariant, results]] of Object.entries(
+      data
+    ).entries()) {
+      const graphData = [];
+      const sorted = [...results].sort((a, b) => {
+        const nodeA = nodemap[getNodeForResult(a)];
+        const nodeB = nodemap[getNodeForResult(b)];
+        return nodeA - nodeB;
+      });
+      for (const result of sorted) {
+        const label = result["bin-env-vars"]["hg"]["version"].split("+")[0];
+        graphData.push({
+          x: label,
+          y: result["result"]["time"]["median"],
+          node: getNodeForResult(result),
+        });
+        labels.add(label);
+      }
+      datasets.push({
+        label: repoVariant,
+        data: graphData,
+        backgroundColor: COLORS[index],
+        borderColor: COLORS[index],
+        borderWidth: 1,
       });
     }
-    datasets.push({
-      label: repoName,
-      data: graphData,
-      backgroundColor: "rgba(255, 99, 132, 0.2)",
-      borderColor: "rgba(255, 99, 132, 1)",
-      borderWidth: 1,
-    });
 
+    const sortedLabels = Array.from(labels);
+    sortedLabels.sort(sortVersions);
     new Chart(ctx, {
       type: "line",
       data: {
+        labels: sortedLabels,
         datasets: datasets,
       },
       options: {
diff --git a/web-ui/src/utils.ts b/web-ui/src/utils.ts
new file mode 100644
--- /dev/null
+++ b/web-ui/src/utils.ts
@@ -0,0 +1,57 @@
+/**
+ * return a sortable key for a given Mercurial version
+ */
+export const parseVersion = (version: string): Array<number> => {
+    let v_parts: string;
+    let rank_s: string;
+    if (version.includes("+")) {
+        let [first, ...rest] = version.split("+");
+        v_parts = first;
+        rank_s = version.slice(first.length + 1);
+    } else {
+        v_parts = version;
+        rank_s = "0";
+    }
+
+    let rc = "100";
+    const pieces = [];
+    if (v_parts.includes("rc")) {
+        let [first, ...rest] = version.split("rc", 1);
+        v_parts = first;
+        rc = version.slice(first.length + 2);
+    }
+
+    const v = v_parts.split(".");
+    pieces.push(...v.map((part: string) => parseInt(part, 10)));
+    while (pieces.length < 3) {
+        pieces.push(0);
+    }
+    pieces.push(parseInt(rc, 10));
+
+    rank_s = rank_s.split(".")[0];
+    rank_s = rank_s.split("-")[0];
+
+    if (rank_s.includes("hg")) {
+        rank_s = rank_s.slice(rank_s.split("hg", 1)[0].length + 2);
+    }
+    pieces.push(parseInt(rank_s, 10));
+    return pieces;
+};
+
+export const sortVersions = (a: string, b: string): number => {
+    const versionA = parseVersion(a);
+    const versionB = parseVersion(b);
+
+    for (const [index, num] of Object.entries(versionA)) {
+        if (num < versionB[index]) {
+            return -1;
+        } else if (num > versionB[index]) {
+            return 1;
+        }
+    }
+    return 0;
+};
+
+export const getNodeForResult = (result) => {
+    return result["bin-env-vars"]["hg"]["changeset"]["node"];
+};
\ No newline at end of file