autotest: delete a bunch of graph-related tko gwt code

BUG=None
TEST=./utils/compile_gwt_clients -a succeeds

Change-Id: I376620eb724d3e45922ced2da46efb31de7f8b44
Reviewed-on: https://chromium-review.googlesource.com/505249
Commit-Ready: Aviv Keshet <akeshet@chromium.org>
Tested-by: Aviv Keshet <akeshet@chromium.org>
Reviewed-by: Keith Haddow <haddowk@chromium.org>
Reviewed-by: Simran Basi <sbasi@chromium.org>
Reviewed-by: Dan Shi <dshi@google.com>
diff --git a/frontend/client/src/autotest/public/EmbeddedTkoClientTest.html b/frontend/client/src/autotest/public/EmbeddedTkoClientTest.html
index 0acea9b..1b880c1 100644
--- a/frontend/client/src/autotest/public/EmbeddedTkoClientTest.html
+++ b/frontend/client/src/autotest/public/EmbeddedTkoClientTest.html
@@ -9,35 +9,12 @@
   <script type="text/javascript">
     function initialize() {
       Autotest.initialize("http://autotest");
-
-      var plot1 = Autotest.createMetricsPlot(document.getElementById("plot1_canvas"));
-      queries = {}
-      queries["__main__"] = "SELECT test_name, AVG(IF(kernel LIKE '2.6.11%', iteration_value, NULL)) '2.6.11', STDDEV(IF(kernel LIKE '2.6.11%', iteration_value, NULL)) 'errors-2.6.11', AVG(IF(kernel LIKE '2.6.18%', iteration_value, NULL)) '2.6.18', STDDEV(IF(kernel LIKE '2.6.18%', iteration_value, NULL)) 'errors-2.6.18' FROM tko_perf_view_2 WHERE test_idx < 1000 AND test_name IN ('dbench', 'tbench') AND iteration_key = 'throughput' AND (kernel LIKE '2.6.11%' OR kernel LIKE '2.6.18%') GROUP BY test_name";
-      queries["__2.6.11__"] = "SELECT test_idx, iteration_value FROM tko_perf_view_2 WHERE test_idx < 1000 AND test_name IN ('dbench', 'tbench') AND iteration_key = 'throughput' AND kernel LIKE '2.6.11%%' AND test_name = %s ORDER BY iteration_value";
-      queries["__2.6.18__"] = "SELECT test_idx, iteration_value FROM tko_perf_view_2 WHERE test_idx < 1000 AND test_name IN ('dbench', 'tbench') AND iteration_key = 'throughput' AND kernel LIKE '2.6.18%%' AND test_name = %s ORDER BY iteration_value";
-      plot1.refresh({
-          plot : "Bar",
-          invert : [],
-          queries : queries
-      });
-
-      var plot2 = Autotest.createMetricsPlot(document.getElementById("plot2_canvas"));
-      queries = {}
-      queries["__main__"] = "SELECT kernel, AVG(iteration_value) 'throughput', STDDEV(iteration_value) 'errors-throughput' FROM tko_perf_view_2 WHERE test_idx < 1000 AND test_name = 'dbench' AND iteration_key ='throughput' GROUP BY kernel";
-      queries["__throughput__"] = "SELECT test_idx, iteration_value FROM tko_perf_view_2 WHERE test_idx < 1000 AND test_name = 'dbench' AND iteration_key ='throughput' AND kernel = %s ORDER BY iteration_value";
-      plot2.refresh({
-          plot : "Line",
-          invert : [],
-          queries : queries
-      });
     }
   </script>
 </head>
 
 <body onload="initialize()">
   Top text
-  <div id="plot1_canvas"></div>
   Middle text
-  <div id="plot2_canvas"></div>
   Bottom text
 </body>
diff --git a/frontend/client/src/autotest/tko/DynamicGraphingFrontend.java b/frontend/client/src/autotest/tko/DynamicGraphingFrontend.java
deleted file mode 100644
index 64536ba..0000000
--- a/frontend/client/src/autotest/tko/DynamicGraphingFrontend.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package autotest.tko;
-
-import autotest.common.JsonRpcCallback;
-import autotest.common.ui.TabView;
-import autotest.tko.PreconfigSelector.PreconfigHandler;
-import autotest.tko.TableView.TableSwitchListener;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-
-import java.util.Map;
-
-public abstract class DynamicGraphingFrontend extends GraphingFrontend
-                                              implements ClickHandler, PreconfigHandler {
-    protected PreconfigSelector preconfig;
-    protected Button graphButton = new Button("Graph");
-    protected Plot plot;
-    private TabView parent;
-
-    public DynamicGraphingFrontend(final TabView parent, Plot plot, String preconfigType) {
-        this.parent = parent;
-        this.plot = plot;
-        plot.setDrilldownTrigger();
-        preconfig = new PreconfigSelector(preconfigType, this);
-        graphButton.addClickHandler(this);
-    }
-
-    @Override
-    public void onClick(ClickEvent event) {
-        if (event.getSource() != graphButton) {
-            super.onClick(event);
-            return;
-        }
-
-        parent.updateHistory();
-        plot.setVisible(false);
-        embeddingLink.setVisible(false);
-        graphButton.setEnabled(false);
-
-        JSONObject params = buildParams();
-        if (params == null) {
-            graphButton.setEnabled(true);
-            return;
-        }
-
-        plot.refresh(params, new JsonRpcCallback() {
-            @Override
-            public void onSuccess(JSONValue result) {
-                plot.setVisible(true);
-                embeddingLink.setVisible(true);
-                graphButton.setEnabled(true);
-            }
-
-            @Override
-            public void onError(JSONObject errorObject) {
-                super.onError(errorObject);
-                graphButton.setEnabled(true);
-            }
-        });
-    }
-
-    protected abstract JSONObject buildParams();
-
-    @Override
-    public void refresh() {
-        // Nothing to refresh
-    }
-
-    protected void commonInitialization() {
-        table.setWidget(table.getRowCount(), 1, graphButton);
-        table.setWidget(table.getRowCount(), 0, plot);
-        table.getFlexCellFormatter().setColSpan(table.getRowCount() - 1, 0, 3);
-
-        table.setWidget(table.getRowCount(), 2, embeddingLink);
-        table.getFlexCellFormatter().setHorizontalAlignment(
-                table.getRowCount() - 1, 2, HasHorizontalAlignment.ALIGN_RIGHT);
-
-        plot.setVisible(false);
-        embeddingLink.setVisible(false);
-
-        initWidget(table);
-    }
-
-    public void handlePreconfig(Map<String, String> preconfigParameters) {
-        handleHistoryArguments(preconfigParameters);
-    }
-
-    @Override
-    protected void setListener(TableSwitchListener listener) {
-        super.setListener(listener);
-        plot.setListener(listener);
-    }
-}
diff --git a/frontend/client/src/autotest/tko/EmbeddedTkoClient.java b/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
index 0ed0a0b..08ba372 100644
--- a/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
+++ b/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
@@ -19,30 +19,8 @@
     public void onModuleLoad() {
         JsonRpcProxy.setDefaultBaseUrl(JsonRpcProxy.TKO_BASE_URL);
         testDetailView = new TestDetailView();
-        injectNativeMethods();
     }
 
-    /**
-     * Creates a global object named Autotest with the following methods.  This objects acts as the
-     * entry point for externally-written native code to create embeddable widgets.
-     * * initialize(autoservServerUrl) -- must call this before anything else, passing in the URL
-     *   to the Autotest server (i.e. "http://myhost").
-     * * createMetricsPlot(parent) -- returns a metrics plot object attached to the given parent 
-     *   element.
-     */
-    private native void injectNativeMethods() /*-{
-        var instance = this;
-        $wnd.Autotest = {
-            initialize: function(autotestServerUrl) {
-                instance.@autotest.tko.EmbeddedTkoClient::initialize(Ljava/lang/String;)(autotestServerUrl);
-            },
-
-            createMetricsPlot: function(parent) {
-                return instance.@autotest.tko.EmbeddedTkoClient::createMetricsPlot(Lcom/google/gwt/dom/client/Element;)(parent);
-            }
-        }
-    }-*/;
-
     @SuppressWarnings("unused") // called from native
     private void initialize(String autotestServerUrl) {
         this.autotestServerUrl = autotestServerUrl;
@@ -51,32 +29,6 @@
         JsonRpcProxy.setProxy(JsonRpcProxy.TKO_BASE_URL, proxy);
     }
 
-    @SuppressWarnings("unused") // called from native
-    private JavaScriptObject createMetricsPlot(Element parent) {
-        UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
-        if (handler == null) {
-            return doCreateMetricsPlot(parent);
-        }
-
-        try {
-            return doCreateMetricsPlot(parent);
-        } catch (Throwable throwable) {
-            handler.onUncaughtException(throwable);
-            return null;
-        }
-    }
-
-    private JavaScriptObject doCreateMetricsPlot(Element parent) {
-        if (parent == null) {
-            throw new IllegalArgumentException("parent element cannot be null");
-        }
-        Plot plot = new MetricsPlot();
-        plot.setDrilldownTrigger();
-        plot.setListener(this);
-        parent.appendChild(plot.getElement());
-        return plot.getNativeProxy();
-    }
-
     public HistoryToken getSelectTestHistoryToken(int testId) {
         testDetailView.updateObjectId(Integer.toString(testId));
         return testDetailView.getHistoryArguments();
diff --git a/frontend/client/src/autotest/tko/ExistingGraphsFrontend.java b/frontend/client/src/autotest/tko/ExistingGraphsFrontend.java
deleted file mode 100644
index 57faee6..0000000
--- a/frontend/client/src/autotest/tko/ExistingGraphsFrontend.java
+++ /dev/null
@@ -1,300 +0,0 @@
-package autotest.tko;
-
-import autotest.common.JsonRpcCallback;
-import autotest.common.SimpleCallback;
-import autotest.common.StaticDataRepository;
-import autotest.common.Utils;
-import autotest.common.ui.TabView;
-
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.json.client.JSONArray;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONString;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import com.google.gwt.user.client.ui.TextBox;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class ExistingGraphsFrontend extends GraphingFrontend {
-
-    private CheckBox normalize = new CheckBox("Normalize Performance (allows multiple benchmarks" +
-                                              " on one graph)");
-    private MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
-    private TextBox hostname = new TextBox();
-    private SuggestBox hostnameSuggest = new SuggestBox(oracle, hostname);
-    private Panel benchmarkWrapper = new HorizontalPanel();
-    private TextBox kernel = new TextBox();
-    private JSONObject hostsAndTests = null;
-    private Button graphButton = new Button("Graph");
-    
-    private ListBox singleBenchmark = new ListBox(false);
-    private ListBox multiBenchmark = new ListBox(true);
-
-    public ExistingGraphsFrontend(final TabView parent) {
-        normalize.addClickHandler(new ClickHandler() {
-            public void onClick(ClickEvent event) {
-                normalizeClicked();
-            }
-        });
-        
-        hostnameSuggest.getTextBox().addBlurHandler(new BlurHandler() {
-            public void onBlur(BlurEvent event) {
-                refreshTests();
-            }
-        });
-        hostnameSuggest.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
-            public void onSelection(SelectionEvent<SuggestOracle.Suggestion> event) {
-                refreshTests();
-            }
-        });
-
-        addBenchmarkItem("(Please select a hostname first)");
-        
-        graphButton.addClickHandler(new ClickHandler() {
-            public void onClick(ClickEvent event) {
-                parent.updateHistory();
-                showGraph();
-            }
-        });
-
-        kernel.setText("all");
-        
-        table.setWidget(0, 0, normalize);
-        table.getFlexCellFormatter().setColSpan(0, 0, 2);
-        benchmarkWrapper.add(singleBenchmark);
-        benchmarkWrapper.add(multiBenchmark);
-        multiBenchmark.setVisible(false);
-        
-        addControl("Hostname:", hostnameSuggest);
-        addControl("Benchmark:", benchmarkWrapper);
-        addControl("Kernel:", kernel);
-        table.setWidget(table.getRowCount(), 1, graphButton);
-
-        table.getColumnFormatter().setWidth(0, "1px");
-        
-        initWidget(table);
-    }
-    
-    private void addBenchmarkItem(String item) {
-        singleBenchmark.addItem(item);
-        multiBenchmark.addItem(item);
-    }
-
-    private void getHostsAndTests(final SimpleCallback onFinished) {
-        setEnabled(false);
-        rpcProxy.rpcCall("get_hosts_and_tests", new JSONObject(), new JsonRpcCallback() {
-            @Override
-            public void onSuccess(JSONValue result) {
-                hostsAndTests = result.isObject();
-                onFinished.doCallback(null);
-                setEnabled(true);
-            }
-        });
-    }
-
-    @Override
-    public void refresh() {
-        getHostsAndTests(new SimpleCallback() {
-            public void doCallback(Object source) {
-                oracle.clear();
-                for (String host : hostsAndTests.keySet()) {
-                    oracle.add(host);
-                }
-            } 
-        });
-    }
-
-    @Override
-    public void addToHistory(Map<String, String> args) {
-        args.put("normalize", String.valueOf(normalize.getValue()));
-        args.put("hostname", hostname.getText());
-
-        // Add the selected benchmarks
-        StringBuilder benchmarks = new StringBuilder();
-        ListBox benchmark = getVisibleBenchmark();
-        for (int i = 0; i < benchmark.getItemCount(); i++) {
-            if (benchmark.isItemSelected(i)) {
-                benchmarks.append(benchmark.getValue(i));
-                benchmarks.append(",");
-            }
-        }
-
-        args.put("benchmark", benchmarks.toString());
-        args.put("kernel", kernel.getText());
-    }
-
-    @Override
-    public void handleHistoryArguments(final Map<String, String> args) {
-        hostname.setText(args.get("hostname"));
-        normalize.setValue(Boolean.parseBoolean(args.get("normalize")));
-        normalizeClicked();
-        kernel.setText(args.get("kernel"));
-
-        getHostsAndTests(new SimpleCallback() {
-            public void doCallback(Object source) {
-                refreshTests();
-                
-                ListBox benchmark = getVisibleBenchmark();
-                Set<String> benchmarks =
-                    new HashSet<String>(Arrays.asList(args.get("benchmark").split(",")));
-                for (int i = 0; i < benchmark.getItemCount(); i++) {
-                    benchmark.setItemSelected(i, benchmarks.contains(benchmark.getValue(i)));
-                }
-            } 
-        });
-    }
-    
-    private ListBox getVisibleBenchmark() {
-        boolean multiVisible = normalize.getValue();
-        if (multiVisible) {
-            assert multiBenchmark.isVisible();
-            return multiBenchmark;
-        } else {
-            assert singleBenchmark.isVisible();
-            return singleBenchmark;
-        }
-    }
-
-    @Override
-    protected void addAdditionalEmbeddingParams(JSONObject params) {
-        // No embedding
-    }
-
-    // Change the state of the page based on the status of the "normalize" checkbox
-    private void normalizeClicked() {
-        boolean multiVisible = normalize.getValue();
-        ListBox dest;
-        ListBox src;
-        
-        if (multiVisible) {
-            dest = multiBenchmark;
-            src = singleBenchmark;
-        } else {
-            dest = singleBenchmark;
-            src = multiBenchmark;
-        }
-        
-        dest.setVisible(true);
-        src.setVisible(false);
-        
-        dest.setSelectedIndex(src.getSelectedIndex());
-        src.setSelectedIndex(-1);
-    }
-
-    private void setEnabled(boolean enabled) {
-        normalize.setEnabled(enabled);
-        hostname.setEnabled(enabled);
-        singleBenchmark.setEnabled(enabled);
-        multiBenchmark.setEnabled(enabled);
-        kernel.setEnabled(enabled);
-        graphButton.setEnabled(enabled);
-    }
-    
-    private void refreshTests() {
-        JSONValue value = hostsAndTests.get(hostnameSuggest.getText());
-        if (value == null) {
-            return;
-        }
-
-        HashSet<String> selectedTests = new HashSet<String>();
-        ListBox benchmark = getVisibleBenchmark();
-        for (int i = 0; i < benchmark.getItemCount(); i++) {
-            if (benchmark.isItemSelected(i)) {
-                selectedTests.add(benchmark.getValue(i));
-            }
-        }
-        
-        JSONArray tests = value.isObject().get("tests").isArray();
-        singleBenchmark.clear();
-        multiBenchmark.clear();
-        for (int i = 0; i < tests.size(); i++) {
-            String test = Utils.jsonToString(tests.get(i));
-            addBenchmarkItem(test);
-            if (selectedTests.contains(test)) {
-                benchmark.setItemSelected(i, true);
-            }
-        }
-    }
-    
-    private void showGraph() {
-        String hostnameStr = hostnameSuggest.getText();
-        
-        JSONValue value = hostsAndTests.get(hostnameStr);
-        if (value == null) {
-            return;
-        }
-        
-        String url;
-        HashMap<String, String> args = new HashMap<String, String>();
-        args.put("kernel", kernel.getText());
-        
-        if (normalize.getValue()) {
-            url = "/tko/machine_aggr.cgi?";
-            final JSONArray tests = new JSONArray();
-            for (int i = 0; i < multiBenchmark.getItemCount(); i++) {
-                if (multiBenchmark.isItemSelected(i)) {
-                    tests.set(tests.size(), new JSONString(multiBenchmark.getValue(i)));
-                }
-            }
-            
-            args.put("machine", hostnameStr);
-
-            StringBuilder arg = new StringBuilder();
-            for (int i = 0; i < tests.size(); i++) {
-                String test = Utils.jsonToString(tests.get(i));
-                String key = getKey(test);
-                if (i != 0) {
-                    arg.append(",");
-                }
-                arg.append(test);
-                arg.append(":");
-                arg.append(key);
-            }
-            args.put("benchmark_key", arg.toString());
-        } else {
-            int benchmarkIndex = singleBenchmark.getSelectedIndex();
-            if (benchmarkIndex == -1) {
-                return;
-            }
-            
-            url = "/tko/machine_test_attribute_graph.cgi?";
-            
-            JSONObject hostObject = value.isObject();
-            String machine = Utils.jsonToString(hostObject.get("id"));
-            String benchmarkStr = singleBenchmark.getValue(benchmarkIndex);
-            
-            args.put("machine", machine);
-            args.put("benchmark", benchmarkStr);
-            args.put("key", getKey(benchmarkStr));
-        }
-        Utils.openUrlInNewWindow(url + Utils.encodeUrlArguments(args));
-    }
-    
-    private String getKey(String benchmark) {
-        JSONObject benchmarkKey =
-            StaticDataRepository.getRepository().getData("benchmark_key").isObject();
-        return Utils.jsonToString(benchmarkKey.get(benchmark.replaceAll("\\..*", "")));
-    }
-
-    @Override
-    public String getFrontendId() {
-        return "existing_graphs";
-    }
-}
diff --git a/frontend/client/src/autotest/tko/GraphingFrontend.java b/frontend/client/src/autotest/tko/GraphingFrontend.java
deleted file mode 100644
index 7b4dcad..0000000
--- a/frontend/client/src/autotest/tko/GraphingFrontend.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package autotest.tko;
-
-import autotest.common.CustomHistory;
-import autotest.common.JsonRpcCallback;
-import autotest.common.JsonRpcProxy;
-import autotest.common.Utils;
-import autotest.common.CustomHistory.CustomHistoryListener;
-import autotest.common.CustomHistory.HistoryToken;
-import autotest.common.ui.NotifyManager;
-import autotest.common.ui.SimpleDialog;
-import autotest.tko.TableView.TableSwitchListener;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.json.client.JSONNumber;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONString;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.HasVerticalAlignment;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwt.user.client.ui.Widget;
-
-import java.util.Map;
-
-public abstract class GraphingFrontend extends Composite
-                                       implements CustomHistoryListener, ClickHandler {
-    public static final String HISTORY_TOKEN = "embedded_query";
-
-    protected FlexTable table = new FlexTable();
-    protected JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();
-    protected Anchor embeddingLink = new Anchor("[Link to this graph]");
-    protected TableSwitchListener listener;
-
-    public abstract void refresh();
-    public abstract void addToHistory(Map<String, String> args);
-    public abstract void handleHistoryArguments(Map<String, String> args);
-
-    /**
-     * This function allows subclasses to add parameters to the call to get_embedding_id() RPC,
-     * called when a user requests an embeddable link to a graph.
-     */
-    protected abstract void addAdditionalEmbeddingParams(JSONObject params);
-
-    /**
-     * @return a short text ID for the frontend
-     */
-    public abstract String getFrontendId();
-
-    protected GraphingFrontend() {
-        CustomHistory.addHistoryListener(this);
-        embeddingLink.addClickHandler(this);
-    }
-
-    public void onClick(ClickEvent event) {
-        assert event.getSource() == embeddingLink;
-        JSONObject params = new JSONObject();
-        params.put("url_token", new JSONString(CustomHistory.getLastHistoryToken().toString()));
-        addAdditionalEmbeddingParams(params);
-
-        rpcProxy.rpcCall("get_embedding_id", params, new JsonRpcCallback() {
-            @Override
-            public void onSuccess(JSONValue result) {
-                String id = Utils.jsonToString(result);
-                showEmbeddedGraphHtml(id);
-            }
-        });
-    }
-
-    private void showEmbeddedGraphHtml(String embeddedGraphId) {
-        StringBuilder link = new StringBuilder();
-        link.append("<a href=\"http://");
-        link.append(Window.Location.getHost());
-        link.append(Window.Location.getPath());
-        link.append("#");
-        link.append(HISTORY_TOKEN);
-        link.append("=");
-        link.append(embeddedGraphId);
-
-        link.append("\"><img border=\"0\" src=\"http://");
-        link.append(Window.Location.getHost());
-
-        link.append(JsonRpcProxy.TKO_BASE_URL);
-        link.append("plot/?id=");
-        link.append(embeddedGraphId);
-
-        link.append("&max_age=10");
-
-        link.append("\"></a>");
-
-        TextBox linkBox = new TextBox();
-        linkBox.setText(link.toString());
-        linkBox.setWidth("100%");
-        linkBox.setSelectionRange(0, link.length());
-
-        new SimpleDialog("Paste HTML to embed in website:", linkBox).center();
-    }
-
-    protected void setListener(TableSwitchListener listener) {
-        this.listener = listener;
-    }
-
-    protected void addControl(String text, Widget control) {
-        int row = TkoUtils.addControlRow(table, text, control);
-        table.getFlexCellFormatter().setColSpan(row, 1, 2);
-        table.getFlexCellFormatter().setWidth(row, 1, "100%");
-        table.getFlexCellFormatter().setVerticalAlignment(row, 0, HasVerticalAlignment.ALIGN_TOP);
-    }
-
-    // TODO(showard): merge this with the code from SavedQueriesControl
-    public void onHistoryChanged(Map<String, String> arguments) {
-        final String idString = arguments.get(HISTORY_TOKEN);
-        if (idString == null) {
-            return;
-        }
-
-        JSONObject args = new JSONObject();
-        args.put("id", new JSONNumber(Integer.parseInt(idString)));
-        rpcProxy.rpcCall("get_embedded_query_url_token", args, new JsonRpcCallback() {
-            @Override
-            public void onSuccess(JSONValue result) {
-                String tokenString = Utils.jsonToString(result);
-                HistoryToken token;
-                try {
-                    token = HistoryToken.fromString(tokenString);
-                } catch (IllegalArgumentException exc) {
-                    NotifyManager.getInstance().showError("Invalid embedded query token " +
-                                                          tokenString);
-                    return;
-                }
-
-                // since this is happening asynchronously, the history may have changed, so ensure
-                // it's set back to what it should be.
-                HistoryToken shortToken = new HistoryToken();
-                shortToken.put(HISTORY_TOKEN, idString);
-                CustomHistory.newItem(shortToken);
-
-                CustomHistory.simulateHistoryToken(token);
-            }
-        });
-    }
-}
diff --git a/frontend/client/src/autotest/tko/GraphingView.java b/frontend/client/src/autotest/tko/GraphingView.java
deleted file mode 100644
index 3b0dade..0000000
--- a/frontend/client/src/autotest/tko/GraphingView.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package autotest.tko;
-
-import autotest.common.CustomHistory.HistoryToken;
-import autotest.common.ui.ExtendedListBox;
-import autotest.common.ui.TabView;
-import autotest.tko.TableView.TableSwitchListener;
-
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.user.client.ui.DeckPanel;
-
-import java.util.Map;
-
-public class GraphingView extends TabView {
-
-    private ExtendedListBox frontendSelection = new ExtendedListBox();
-    private MetricsPlotFrontend metricsPlotFrontend = new MetricsPlotFrontend(this);
-    private MachineQualHistogramFrontend machineQualHistogramFrontend =
-        new MachineQualHistogramFrontend(this);
-    private ExistingGraphsFrontend existingGraphsFrontend = new ExistingGraphsFrontend(this);
-    private DeckPanel controlPanel = new DeckPanel();
-    private GraphingFrontend frontends[] = {
-            metricsPlotFrontend,
-            machineQualHistogramFrontend,
-            existingGraphsFrontend,
-    };
-
-    public GraphingView(TableSwitchListener listener) {
-        metricsPlotFrontend.setListener(listener);
-        machineQualHistogramFrontend.setListener(listener);
-    }
-    
-    @Override
-    public void initialize() {
-        super.initialize();
-        frontendSelection.addItem("Metrics Plot", metricsPlotFrontend.getFrontendId());
-        frontendSelection.addItem("Machine Qualification Histogram", 
-                                  machineQualHistogramFrontend.getFrontendId());
-        frontendSelection.addItem("Existing Graphs", existingGraphsFrontend.getFrontendId());
-
-        frontendSelection.addChangeHandler(new ChangeHandler() {
-            public void onChange(ChangeEvent event) {
-                showSelectedView();
-                updateHistory();
-            }
-        });
-
-        controlPanel.add(metricsPlotFrontend);
-        controlPanel.add(machineQualHistogramFrontend);
-        controlPanel.add(existingGraphsFrontend);
-        controlPanel.showWidget(0);
-
-        addWidget(frontendSelection, "graphing_type");
-        addWidget(controlPanel, "graphing_frontend");
-    }
-
-    @Override
-    public String getElementId() {
-        return "graphing_view";
-    }
-    
-    private GraphingFrontend getSelectedFrontend() {
-        return frontends[frontendSelection.getSelectedIndex()];
-    }
-
-    @Override
-    public void refresh() {
-        super.refresh();
-        getSelectedFrontend().refresh();
-    }
-
-    @Override
-    public void display() {
-        super.display();
-        CommonPanel.getPanel().setConditionVisible(false);
-    }
-    
-    @Override
-    public HistoryToken getHistoryArguments() {
-        HistoryToken args = super.getHistoryArguments();
-        args.put("view", getSelectedFrontend().getFrontendId());
-        getSelectedFrontend().addToHistory(args);
-        return args;
-    }
-
-    @Override
-    public void handleHistoryArguments(Map<String, String> arguments) {
-        super.handleHistoryArguments(arguments);
-        String frontendId = arguments.get("view");
-        frontendSelection.selectByValue(frontendId);
-        for (GraphingFrontend frontend : frontends) {
-            if (frontend.getFrontendId().equals(frontendId)) {
-                frontend.handleHistoryArguments(arguments);
-                break;
-            }
-        }
-        showSelectedView();
-    }
-    
-    private void showSelectedView() {
-        int index = frontendSelection.getSelectedIndex();
-        controlPanel.showWidget(index);
-        frontends[index].refresh();
-    }
-}
diff --git a/frontend/client/src/autotest/tko/MachineQualHistogram.java b/frontend/client/src/autotest/tko/MachineQualHistogram.java
deleted file mode 100644
index 757553e..0000000
--- a/frontend/client/src/autotest/tko/MachineQualHistogram.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package autotest.tko;
-
-import autotest.common.Utils;
-import autotest.common.ui.SimpleDialog;
-import autotest.tko.TableView.TableViewConfig;
-
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.user.client.ui.HTML;
-
-class MachineQualHistogram extends Plot {
-    public MachineQualHistogram() {
-        super("create_qual_histogram");
-    }
-
-    /**
-     * drilldownParams contains:
-     * * type: "normal", "not_applicable", or "empty"
-     * for type "normal":
-     *   * filterString: SQL filter for selected bucket
-     * for type "not_applicable":
-     *   * hosts: HTML list of hosts in this bucket
-     */
-    @Override
-    protected void showDrilldownImpl(JSONObject drilldownParams) {
-        String type = Utils.jsonToString(drilldownParams.get("type"));
-        if (type.equals("normal")) {
-            String filterString = Utils.jsonToString(drilldownParams.get("filterString"));
-            showNormalDrilldown(filterString);
-        } else if (type.equals("not_applicable")) {
-            String hosts = Utils.jsonToString(drilldownParams.get("hosts"));
-            showNADialog(hosts);
-        } else if (type.equals("empty")) {
-            showEmptyDialog();
-        }
-    }
-
-    private void showNormalDrilldown(final String filterString) {
-        CommonPanel.getPanel().setSqlCondition(filterString);
-        listener.onSwitchToTable(TableViewConfig.PASS_RATE);
-    }
-
-    private void showNADialog(String hosts) {
-        new SimpleDialog("Did not run any of the selected tests:", new HTML(hosts)).center();
-    }
-
-    private void showEmptyDialog() {
-        new SimpleDialog("No hosts in this pass rate range", new HTML()).center();
-    }
-}
diff --git a/frontend/client/src/autotest/tko/MachineQualHistogramFrontend.java b/frontend/client/src/autotest/tko/MachineQualHistogramFrontend.java
deleted file mode 100644
index 5d226cd..0000000
--- a/frontend/client/src/autotest/tko/MachineQualHistogramFrontend.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package autotest.tko;
-
-import autotest.common.ui.NotifyManager;
-import autotest.common.ui.TabView;
-
-import com.google.gwt.json.client.JSONNumber;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONString;
-import com.google.gwt.user.client.ui.TextBox;
-
-import java.util.Map;
-
-public class MachineQualHistogramFrontend extends DynamicGraphingFrontend {
-    private static final String DEFAULT_INTERVAL = "10";
-
-    private FilterSelector globalFilters =
-        new FilterSelector(DBColumnSelector.TEST_VIEW);
-    private FilterSelector testFilters =
-        new FilterSelector(DBColumnSelector.TEST_VIEW);
-    private TextBox interval = new TextBox();
-
-    public MachineQualHistogramFrontend(final TabView parent) {
-        super(parent, new MachineQualHistogram(), "qual");
-
-        interval.setText(DEFAULT_INTERVAL);
-
-        addControl("Preconfigured:", preconfig);
-        addControl("Global filters:", globalFilters);
-        addControl("Test set filters:", testFilters);
-        addControl("Interval:", interval);
-        
-        commonInitialization();
-    }
-    
-    @Override
-    protected void addAdditionalEmbeddingParams(JSONObject params) {
-        params.put("graph_type", new JSONString("qual"));
-        params.put("params", buildParams());
-    }
-    
-    private JSONString buildQuery() {
-        String gFilterString = globalFilters.getFilterString();
-        String tFilterString = testFilters.getFilterString();
-        boolean hasGFilter = !gFilterString.equals("");
-        boolean hasTFilter = !tFilterString.equals("");
-        
-        StringBuilder sql = new StringBuilder();
-        
-        sql.append("SELECT hostname, COUNT(DISTINCT ");
-        if (hasTFilter) {
-            sql.append("IF(");
-            sql.append(tFilterString);
-            sql.append(", test_idx, NULL)");
-        } else {
-            sql.append("test_idx");
-        }
-        sql.append(") 'total', COUNT(DISTINCT IF(");
-        if (hasTFilter) {
-            sql.append(TkoUtils.wrapWithParens(tFilterString));
-            sql.append(" AND ");
-        }
-        
-        sql.append("status = 'GOOD', test_idx, NULL)) 'good' FROM tko_test_view_outer_joins");
-        if (hasGFilter) {
-            sql.append(" WHERE ");
-            sql.append(gFilterString);
-        }
-        sql.append(" GROUP BY hostname");
-        return new JSONString(sql.toString());
-    }
-    
-    private JSONString buildFilterString() {
-        StringBuilder filterString = new StringBuilder();
-        String gFilterString = globalFilters.getFilterString();
-        String tFilterString = testFilters.getFilterString();
-        boolean hasGFilter = !gFilterString.equals("");
-        boolean hasTFilter = !tFilterString.equals("");
-        if (hasGFilter) {
-            filterString.append(TkoUtils.wrapWithParens(gFilterString));
-            if (hasTFilter) {
-                filterString.append(" AND ");
-            }
-        }
-        if (hasTFilter) {
-            filterString.append(TkoUtils.wrapWithParens(tFilterString));
-        }
-        return new JSONString(filterString.toString());
-    }
-    
-    @Override
-    protected JSONObject buildParams() {
-        if (interval.getText().equals("")) {
-            NotifyManager.getInstance().showError("You must enter an interval");
-            return null;
-        }
-        
-        int intervalValue;
-        try {
-            intervalValue = Integer.parseInt(interval.getText());
-        } catch (NumberFormatException e) {
-            NotifyManager.getInstance().showError("Interval must be an integer");
-            return null;
-        }
-        
-        JSONObject params = new JSONObject();
-        params.put("query", buildQuery());
-        params.put("filter_string", buildFilterString());
-        params.put("interval", new JSONNumber(intervalValue));
-
-        return params;
-    }
-
-    @Override
-    public String getFrontendId() {
-        return "machine_qual_histogram";
-    }
-    
-    @Override
-    public void addToHistory(Map<String, String> args) {
-        globalFilters.addToHistory(args, "globalFilter");
-        testFilters.addToHistory(args, "testFilter");
-        args.put("interval", interval.getText());
-    }
-    
-    @Override
-    public void handleHistoryArguments(Map<String, String> args) {
-        setVisible(false);
-        globalFilters.reset();
-        testFilters.reset();
-        globalFilters.handleHistoryArguments(args, "globalFilter");
-        testFilters.handleHistoryArguments(args, "testFilter");
-        interval.setText(args.get("interval"));
-        setVisible(true);
-    }
-}
diff --git a/frontend/client/src/autotest/tko/MetricsPlot.java b/frontend/client/src/autotest/tko/MetricsPlot.java
deleted file mode 100644
index 567dc68..0000000
--- a/frontend/client/src/autotest/tko/MetricsPlot.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package autotest.tko;
-
-import autotest.common.JsonRpcCallback;
-import autotest.common.Utils;
-import autotest.common.ui.SimpleDialog;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.json.client.JSONArray;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONString;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable;
-
-class MetricsPlot extends Plot {
-    public MetricsPlot() {
-        super("create_metrics_plot");
-    }
-
-    /**
-     * drilldownParams contains:
-     * * query - SQL query for the selected series
-     * * series - name of the selected series
-     * * param - parameter to fill in query for the selected data point
-     */
-    @Override
-    protected void showDrilldownImpl(JSONObject drilldownParams) {
-        String query = Utils.jsonToString(drilldownParams.get("query"));
-        final String series = Utils.jsonToString(drilldownParams.get("series"));
-        final String param = Utils.jsonToString(drilldownParams.get("param"));
-
-        JSONObject params = new JSONObject();
-        params.put("query", new JSONString(query));
-        params.put("param", new JSONString(param));
-        rpcProxy.rpcCall("execute_query_with_param", params, new JsonRpcCallback() {
-            @Override
-            public void onSuccess(JSONValue result) {
-                JSONArray data = result.isArray();
-
-                String title = series + " for " + param;
-                FlexTable contents = new FlexTable();
-                final SimpleDialog drill = new SimpleDialog(title, contents);
-
-                for (int i = 0; i < data.size(); i++) {
-                    final JSONArray row = data.get(i).isArray();
-                    final int testId = (int) row.get(0).isNumber().doubleValue();
-                    String yValue = Utils.jsonToString(row.get(1));
-
-                    Anchor link = new Anchor(yValue);
-                    link.addClickHandler(new ClickHandler() {
-                        public void onClick(ClickEvent event) {
-                            drill.hide();
-                            listener.onSelectTest(testId);
-                        }
-                    });
-                    contents.setWidget(i, 0, link);
-                }
-
-                drill.center();
-            }
-        });
-    }
-}
diff --git a/frontend/client/src/autotest/tko/MetricsPlotFrontend.java b/frontend/client/src/autotest/tko/MetricsPlotFrontend.java
deleted file mode 100644
index cd4cd51..0000000
--- a/frontend/client/src/autotest/tko/MetricsPlotFrontend.java
+++ /dev/null
@@ -1,341 +0,0 @@
-package autotest.tko;
-
-import autotest.common.Utils;
-import autotest.common.ui.ExtendedListBox;
-import autotest.common.ui.NotifyManager;
-import autotest.common.ui.TabView;
-import autotest.tko.SeriesSelector.Series;
-
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONString;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.RadioButton;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class MetricsPlotFrontend extends DynamicGraphingFrontend implements ClickHandler {
-    
-    public static final String NORMALIZE_SINGLE = "single";
-    public static final String NORMALIZE_FIRST = "first";
-    public static final String NORMALIZE_SERIES_PREFIX = "series__";
-    public static final String NORMALIZE_X_PREFIX = "x__";
-    
-    protected FilterSelector globalFilter = new FilterSelector(DBColumnSelector.PERF_VIEW);
-    private ExtendedListBox plotSelector = new ExtendedListBox();
-    private ExtendedListBox xAxis = new DBColumnSelector(DBColumnSelector.PERF_VIEW, true);
-    private RadioButton noNormalizeMultiple =
-        new RadioButton("normalize", "No normalization (multiple subplots)");
-    private RadioButton noNormalizeSingle =
-        new RadioButton("normalize", "No normalization (single plot)");
-    private RadioButton normalizeFirst = new RadioButton("normalize", "First data point");
-    private RadioButton normalizeSeries = new RadioButton("normalize", "Specified series:");
-    private ExtendedListBox normalizeSeriesSelect = new ExtendedListBox();
-    private RadioButton normalizeX = new RadioButton("normalize", "Specified X-axis value:");
-    private TextBox normalizeXSelect = new TextBox();
-    private SeriesSelector seriesSelector = new SeriesSelector(new ChangeHandler() {
-        public void onChange(ChangeEvent event) {
-            refreshSeries();
-        }
-    });
-    
-    public MetricsPlotFrontend(final TabView parent) {
-        super(parent, new MetricsPlot(), "metrics");
-
-        noNormalizeSingle.setValue(true);
-
-        noNormalizeMultiple.addClickHandler(this);
-        noNormalizeSingle.addClickHandler(this);
-        normalizeFirst.addClickHandler(this);
-        normalizeSeries.addClickHandler(this);
-        normalizeX.addClickHandler(this);
-
-        normalizeSeriesSelect.setEnabled(false);
-        normalizeXSelect.setEnabled(false);
-
-        plotSelector.addItem("Line");
-        plotSelector.addItem("Bar");
-        plotSelector.addChangeHandler(new ChangeHandler() {
-            public void onChange(ChangeEvent event) {
-                checkNormalizeInput();
-            }
-        });
-
-        Panel normalizePanel = new VerticalPanel();
-        normalizePanel.add(noNormalizeMultiple);
-        normalizePanel.add(noNormalizeSingle);
-        Panel seriesPanel = new HorizontalPanel();
-        seriesPanel.add(normalizeSeries);
-        seriesPanel.add(normalizeSeriesSelect);
-        normalizePanel.add(seriesPanel);
-        normalizePanel.add(normalizeFirst);
-        Panel baselinePanel = new HorizontalPanel();
-        baselinePanel.add(normalizeX);
-        baselinePanel.add(normalizeXSelect);
-        normalizePanel.add(baselinePanel);
-        
-        addControl("Preconfigured:", preconfig);
-        addControl("Plot:", plotSelector);
-        addControl("X-axis values:", xAxis);
-        addControl("Global filters:", globalFilter);
-        addControl("Series:", seriesSelector);
-        addControl("Normalize to:", normalizePanel);
-        
-        commonInitialization();
-    }
-
-    @Override
-    public void onClick(ClickEvent event) {
-        if (event.getSource() != noNormalizeSingle && event.getSource() != noNormalizeMultiple 
-                && event.getSource() != normalizeSeries && event.getSource() != normalizeX) {
-            super.onClick(event);
-            return;
-        }
-
-        normalizeSeriesSelect.setEnabled(false);
-        normalizeXSelect.setEnabled(false);
-        if (event.getSource() == normalizeSeries) {
-            normalizeSeriesSelect.setEnabled(true);
-            refreshSeries();
-        } else if (event.getSource() == normalizeX) {
-            normalizeXSelect.setEnabled(true);
-        }
-
-        checkInvertible();
-    }
-    
-    private void addNormalizeParameter(String plotType, Map<String, String> parameters) {
-        String normalizationType = null;
-        if (plotType.equals("Line") && noNormalizeSingle.getValue()) {
-            normalizationType = NORMALIZE_SINGLE;
-        } else if (normalizeFirst.getValue()) {
-            normalizationType = NORMALIZE_FIRST;
-        } else if (normalizeSeries.getValue()) {
-            String series = normalizeSeriesSelect.getSelectedValue();
-            normalizationType = NORMALIZE_SERIES_PREFIX + series;
-        } else if (normalizeX.getValue()) {
-            String baseline = normalizeXSelect.getText();
-            normalizationType = NORMALIZE_X_PREFIX + baseline;
-        }
-        
-        if (normalizationType != null) {
-            parameters.put("normalize", normalizationType);
-        }
-    }
-
-    @Override
-    public void addToHistory(Map<String, String> args) {
-        String plot = plotSelector.getValue(plotSelector.getSelectedIndex());
-        args.put("plot", plot);
-        args.put("xAxis", xAxis.getSelectedValue());
-        globalFilter.addToHistory(args, "globalFilter");
-        seriesSelector.addToHistory(args);
-        addNormalizeParameter(plot, args);
-    }
-    
-    @Override
-    public void handleHistoryArguments(Map<String, String> args) {
-        setVisible(false);
-        plot.setVisible(false);
-        embeddingLink.setVisible(false);
-        globalFilter.reset();
-        seriesSelector.reset();
-        for (int i = 0; i < plotSelector.getItemCount(); i++) {
-            if (plotSelector.getValue(i).equals(args.get("plot"))) {
-                plotSelector.setSelectedIndex(i);
-                break;
-            }
-        }
-        
-        xAxis.selectByValue(args.get("xAxis"));
-        globalFilter.handleHistoryArguments(args, "globalFilter");
-        seriesSelector.handleHistoryArguments(args);
-        
-        refreshSeries();
-        noNormalizeMultiple.setValue(true);
-        normalizeSeriesSelect.setEnabled(false);
-        normalizeXSelect.setEnabled(false);
-        String normalizeString = args.get("normalize");
-        if (normalizeString != null) {
-            if (normalizeString.equals(NORMALIZE_SINGLE)) {
-                noNormalizeSingle.setValue(true);
-            } else if (normalizeString.equals(NORMALIZE_FIRST)) {
-                normalizeFirst.setValue(true);
-            } else if (normalizeString.startsWith(NORMALIZE_SERIES_PREFIX)) {
-                normalizeSeries.setValue(true);
-                String series = normalizeString.substring(NORMALIZE_SERIES_PREFIX.length());
-                for (int i = 0; i < normalizeSeriesSelect.getItemCount(); i++) {
-                    if (normalizeSeriesSelect.getValue(i).equals(series)) {
-                        normalizeSeriesSelect.setSelectedIndex(i);
-                        break;
-                    }
-                }
-                normalizeSeriesSelect.setEnabled(true);
-            } else if (normalizeString.startsWith(NORMALIZE_X_PREFIX)) {
-                normalizeX.setValue(true);
-                normalizeXSelect.setText(normalizeString.substring(NORMALIZE_X_PREFIX.length()));
-                normalizeXSelect.setEnabled(true);
-            }
-        }
-        checkNormalizeInput();
-        checkInvertible();
-
-        setVisible(true);
-    }
-    
-    @Override
-    protected void addAdditionalEmbeddingParams(JSONObject params) {
-        params.put("graph_type", new JSONString("metrics"));
-        params.put("params", buildParams());
-    }
-    
-    private void refreshSeries() {
-        String selectedValue = normalizeSeriesSelect.getSelectedValue();
-        normalizeSeriesSelect.clear();
-        for (Series selector : seriesSelector.getAllSeries()) {
-            normalizeSeriesSelect.addItem(selector.getName());
-            if (selector.getName().equals(selectedValue)) {
-                normalizeSeriesSelect.setSelectedIndex(normalizeSeriesSelect.getItemCount() - 1);
-            }
-        }
-    }
-
-    // Disable "No Normalization (multiple)" for bar charts
-    private void checkNormalizeInput() {
-        if (plotSelector.getValue(plotSelector.getSelectedIndex()).equals("Line")) {
-            noNormalizeMultiple.setEnabled(true);
-        } else {
-            noNormalizeMultiple.setEnabled(false);
-            if (noNormalizeMultiple.getValue()) {
-                noNormalizeSingle.setValue(true);
-            }
-        }
-    }
-    
-    private JSONObject buildQueries() {
-        List<Series> seriesList = seriesSelector.getAllSeries();
-        JSONObject queries = new JSONObject();
-        StringBuilder sql = new StringBuilder();
-        
-        sql.append("SELECT ");
-        sql.append(xAxis.getSelectedValue());
-        for (Series series : seriesList) {
-            addSeriesSelects(series, sql);
-        }
-        
-        sql.append(" FROM tko_perf_view_2");
-            
-        String xFilterString = globalFilter.getFilterString();
-        if (xFilterString.equals("")) {
-            NotifyManager.getInstance().showError("You must enter a global filter");
-            return null;
-        }
-        
-        sql.append(" WHERE ");
-        sql.append(xFilterString);
-
-        sql.append(" GROUP BY ");
-        sql.append(xAxis.getSelectedValue());
-        queries.put("__main__", new JSONString(sql.toString()));
-        
-        for (Series series : seriesList) {
-            String drilldownQuery = getSeriesDrilldownQuery(series, xFilterString);
-            queries.put("__" + series.getName() + "__", new JSONString(drilldownQuery));
-        }
-        
-        return queries;
-    }
-
-    private String getSeriesDrilldownQuery(Series series, String xFilterString) {
-        StringBuilder sql;
-        sql = new StringBuilder();
-        ExtendedListBox valueSelector = series.getDBColumnSelector();
-        
-        sql.append("SELECT test_idx, ");
-        sql.append(valueSelector.getSelectedValue());
-        sql.append(" FROM tko_perf_view_2 WHERE ");
-        
-        String seriesFilter = series.getFilterString();
-        if (!xFilterString.equals("") || !seriesFilter.equals("")) {
-            sql.append(xFilterString.replace("%", "%%"));
-            if (!xFilterString.equals("") && !seriesFilter.equals("")) {
-                sql.append(" AND ");
-            }
-            sql.append(seriesFilter.replace("%", "%%"));
-            sql.append(" AND ");
-        }
-        
-        sql.append(xAxis.getSelectedValue());
-        sql.append(" = %s ORDER BY ");
-        sql.append(valueSelector.getSelectedValue());
-        return sql.toString();
-    }
-
-    private void addSeriesSelects(Series series, StringBuilder sql) {
-        ExtendedListBox valueSelector = series.getDBColumnSelector();
-        
-        StringBuilder ifClause = new StringBuilder();
-        String seriesFilter = series.getFilterString();
-        if (!seriesFilter.equals("")) {
-            ifClause.append("IF(");
-            ifClause.append(seriesFilter);
-            ifClause.append(", ");
-        }
-        ifClause.append(valueSelector.getSelectedValue());
-        if (!seriesFilter.equals("")) {
-            ifClause.append(", NULL)");   
-        }
-        
-        sql.append(", ");
-        sql.append(series.getAggregation());
-        sql.append(ifClause.toString());
-        sql.append(") '");
-        sql.append(series.getName());
-        sql.append("'");
-        if (series.wantErrorBars()) {
-            sql.append(", STDDEV(");
-            sql.append(ifClause.toString());
-            sql.append(") 'errors-");
-            sql.append(series.getName());
-            sql.append("'");
-        }
-    }
-    
-    // Disable the "Invert y-axis" checkboxes if inversion doesn't make sense
-    private void checkInvertible() {
-        boolean invertible = noNormalizeMultiple.getValue() || normalizeFirst.getValue()
-                             || normalizeX.getValue();
-        seriesSelector.setInvertible(invertible);
-    }
-    
-    @Override
-    protected JSONObject buildParams() {
-        JSONObject queries = buildQueries();
-        if (queries == null) {
-            return null;
-        }
-        
-        Map<String, String> params = new HashMap<String, String>();
-        String plot = plotSelector.getSelectedValue();
-        params.put("plot", plot);
-        addNormalizeParameter(plot, params);
-
-        JSONObject jsonParams = Utils.mapToJsonObject(params);
-        jsonParams.put("invert", Utils.stringsToJSON(seriesSelector.getInverted()));
-        jsonParams.put("queries", queries);
-        return jsonParams;
-    }
-
-    @Override
-    public String getFrontendId() {
-        return "metrics_plot";
-    }
-}
diff --git a/frontend/client/src/autotest/tko/Plot.java b/frontend/client/src/autotest/tko/Plot.java
deleted file mode 100644
index bacf01c..0000000
--- a/frontend/client/src/autotest/tko/Plot.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package autotest.tko;
-
-import autotest.common.JsonRpcCallback;
-import autotest.common.JsonRpcProxy;
-import autotest.common.Utils;
-import autotest.tko.TableView.TableSwitchListener;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONString;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTML;
-
-abstract class Plot extends Composite {
-    private static final String CALLBACK_PREFIX = "__plot_drilldown";
-
-    private static int callbackNameCounter = 0;
-    protected final static JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();
-
-    private String rpcName;
-    private HTML plotElement = new HTML();
-    protected TableSwitchListener listener;
-
-    private String callbackName;
-
-    private static class DummyRpcCallback extends JsonRpcCallback {
-        @Override
-        public void onSuccess(JSONValue result) {}
-    }
-
-    public Plot(String rpcName) {
-        this.rpcName = rpcName;
-        this.callbackName = getFreshCallbackName();
-        initWidget(plotElement);
-    }
-    
-    private static String getFreshCallbackName() {
-        callbackNameCounter++;
-        return CALLBACK_PREFIX + callbackNameCounter;
-    }
-
-    /**
-     * This function is called at initialization time and allows the plot to put native 
-     * callbacks in place for drilldown functionality from graphs.
-     */
-    public native void setDrilldownTrigger() /*-{
-        var instance = this;
-        var name = this.@autotest.tko.Plot::callbackName;
-        $wnd[name] = function(drilldownParams) {
-            instance.@autotest.tko.Plot::showDrilldown(Lcom/google/gwt/core/client/JavaScriptObject;)(drilldownParams);
-        }
-    }-*/;
-
-    /**
-     * Get a native JS object that acts as a proxy to this object.  Currently the only exposed
-     * method is refresh(params), where params is a JS object.  This is only necessary for allowing
-     * externally-written native code to use this object without having to write out the full JSNI
-     * method call syntax.
-     */
-    public native JavaScriptObject getNativeProxy() /*-{
-        var instance = this;
-        return {
-            refresh: function(params) {
-                jsonObjectParams = @com.google.gwt.json.client.JSONObject::new(Lcom/google/gwt/core/client/JavaScriptObject;)(params);
-                instance.@autotest.tko.Plot::refresh(Lcom/google/gwt/json/client/JSONObject;)(jsonObjectParams);
-            }
-        };
-    }-*/;
-
-    @SuppressWarnings("unused") // called from native code (see setDrilldownTrigger)
-    private void showDrilldown(JavaScriptObject drilldownParamsJso) {
-        UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
-        if (handler == null) {
-            showDrilldownImpl(new JSONObject(drilldownParamsJso));
-            return;
-        }
-
-        try {
-            showDrilldownImpl(new JSONObject(drilldownParamsJso));
-        } catch (Throwable throwable) {
-            handler.onUncaughtException(throwable);
-        }
-    }
-
-    protected abstract void showDrilldownImpl(JSONObject drilldownParams);
-
-    public void refresh(JSONObject params, final JsonRpcCallback callback) {
-        params.put("drilldown_callback", new JSONString(callbackName));
-        rpcProxy.rpcCall(rpcName, params, new JsonRpcCallback() {
-            @Override
-            public void onSuccess(JSONValue result) {
-                plotElement.setHTML(Utils.jsonToString(result));
-                callback.onSuccess(result);
-            }
-
-            @Override
-            public void onError(JSONObject errorObject) {
-                callback.onError(errorObject);
-            }
-        });
-    }
-
-    public void refresh(JSONObject params) {
-        refresh(params, new DummyRpcCallback());
-    }
-
-    public void setListener(TableSwitchListener listener) {
-        this.listener = listener;
-    }
-}
diff --git a/frontend/client/src/autotest/tko/TkoClient.java b/frontend/client/src/autotest/tko/TkoClient.java
index f9fb41c..c738bb6 100644
--- a/frontend/client/src/autotest/tko/TkoClient.java
+++ b/frontend/client/src/autotest/tko/TkoClient.java
@@ -19,7 +19,6 @@
     private CommonPanel commonPanel;
     private TabView spreadsheetView;
     private TableView tableView;
-    private GraphingView graphingView;
     private TestDetailView detailView;
     
     private CustomTabPanel mainTabPanel = new CustomTabPanel();
@@ -44,13 +43,11 @@
         commonPanel = CommonPanel.getPanel();
         spreadsheetView = new SpreadsheetView(this);
         tableView = new TableView(this);
-        graphingView = new GraphingView(this);
         detailView = TkoUtils.factory.getTestDetailView();
         
         mainTabPanel.getCommonAreaPanel().add(commonPanel);
         mainTabPanel.addTabView(spreadsheetView);
         mainTabPanel.addTabView(tableView);
-        mainTabPanel.addTabView(graphingView);
         mainTabPanel.addTabView(detailView);
         
         savedQueriesControl = new SavedQueriesControl();
diff --git a/frontend/tko/graphing_utils.py b/frontend/tko/graphing_utils.py
deleted file mode 100644
index 9d1a9fb..0000000
--- a/frontend/tko/graphing_utils.py
+++ /dev/null
@@ -1,861 +0,0 @@
-# pylint: disable-msg=C0111
-
-import base64, os, tempfile, pickle, datetime, django.db
-import os.path, getpass
-from math import sqrt
-
-# When you import matplotlib, it tries to write some temp files for better
-# performance, and it does that to the directory in MPLCONFIGDIR, or, if that
-# doesn't exist, the home directory. Problem is, the home directory is not
-# writable when running under Apache, and matplotlib's not smart enough to
-# handle that. It does appear smart enough to handle the files going
-# away after they are written, though.
-
-temp_dir = os.path.join(tempfile.gettempdir(),
-                        '.matplotlib-%s' % getpass.getuser())
-if not os.path.exists(temp_dir):
-    os.mkdir(temp_dir)
-os.environ['MPLCONFIGDIR'] = temp_dir
-
-try:
-    import matplotlib
-    matplotlib.use('Agg')
-    import matplotlib.figure, matplotlib.backends.backend_agg
-    import StringIO, colorsys, PIL.Image, PIL.ImageChops
-except ImportError:
-    # Do nothing, in case this is part of a unit test, so the unit test
-    # can proceed.
-    pass
-
-from autotest_lib.frontend.afe import readonly_connection
-from autotest_lib.frontend.afe.model_logic import ValidationError
-from json import encoder
-from autotest_lib.client.common_lib import global_config
-from autotest_lib.frontend.tko import models, tko_rpc_utils
-
-_FIGURE_DPI = 100
-_FIGURE_WIDTH_IN = 10
-_FIGURE_BOTTOM_PADDING_IN = 2 # for x-axis labels
-
-_SINGLE_PLOT_HEIGHT = 6
-_MULTIPLE_PLOT_HEIGHT_PER_PLOT = 4
-
-_MULTIPLE_PLOT_MARKER_TYPE = 'o'
-_MULTIPLE_PLOT_MARKER_SIZE = 4
-_SINGLE_PLOT_STYLE = 'bs-' # blue squares with lines connecting
-_SINGLE_PLOT_ERROR_BAR_COLOR = 'r'
-
-_LEGEND_FONT_SIZE = 'xx-small'
-_LEGEND_HANDLE_LENGTH = 0.03
-_LEGEND_NUM_POINTS = 3
-_LEGEND_MARKER_TYPE = 'o'
-
-_LINE_XTICK_LABELS_SIZE = 'x-small'
-_BAR_XTICK_LABELS_SIZE = 8
-
-_json_encoder = encoder.JSONEncoder()
-
-class NoDataError(Exception):
-    """\
-    Exception to raise if the graphing query returned an empty resultset.
-    """
-
-
-def _colors(n):
-    """\
-    Generator function for creating n colors. The return value is a tuple
-    representing the RGB of the color.
-    """
-    for i in xrange(n):
-        yield colorsys.hsv_to_rgb(float(i) / n, 1.0, 1.0)
-
-
-def _resort(kernel_labels, list_to_sort):
-    """\
-    Resorts a list, using a list of kernel strings as the keys. Returns the
-    resorted list.
-    """
-
-    labels = [tko_rpc_utils.KernelString(label) for label in kernel_labels]
-    resorted_pairs = sorted(zip(labels, list_to_sort))
-
-    # We only want the resorted list; we are not interested in the kernel
-    # strings.
-    return [pair[1] for pair in resorted_pairs]
-
-
-def _quote(string):
-    return "%s%s%s" % ("'", string.replace("'", r"\'"), "'")
-
-
-_HTML_TEMPLATE = """\
-<html><head></head><body>
-<img src="data:image/png;base64,%s" usemap="#%s"
-  border="0" alt="graph">
-<map name="%s">%s</map>
-</body></html>"""
-
-_AREA_TEMPLATE = """\
-<area shape="rect" coords="%i,%i,%i,%i" title="%s"
-href="#"
-onclick="%s(%s); return false;">"""
-
-
-class MetricsPlot(object):
-    def __init__(self, query_dict, plot_type, inverted_series, normalize_to,
-                 drilldown_callback):
-        """
-        query_dict: dictionary containing the main query and the drilldown
-            queries.  The main query returns a row for each x value.  The first
-            column contains the x-axis label.  Subsequent columns contain data
-            for each series, named by the column names.  A column named
-            'errors-<x>' will be interpreted as errors for the series named <x>.
-
-        plot_type: 'Line' or 'Bar', depending on the plot type the user wants
-
-        inverted_series: list of series that should be plotted on an inverted
-            y-axis
-
-        normalize_to:
-            None - do not normalize
-            'first' - normalize against the first data point
-            'x__%s' - normalize against the x-axis value %s
-            'series__%s' - normalize against the series %s
-
-        drilldown_callback: name of drilldown callback method.
-        """
-        self.query_dict = query_dict
-        if plot_type == 'Line':
-            self.is_line = True
-        elif plot_type == 'Bar':
-            self.is_line = False
-        else:
-            raise ValidationError({'plot' : 'Plot must be either Line or Bar'})
-        self.plot_type = plot_type
-        self.inverted_series = inverted_series
-        self.normalize_to = normalize_to
-        if self.normalize_to is None:
-            self.normalize_to = ''
-        self.drilldown_callback = drilldown_callback
-
-
-class QualificationHistogram(object):
-    def __init__(self, query, filter_string, interval, drilldown_callback):
-        """
-        query: the main query to retrieve the pass rate information.  The first
-            column contains the hostnames of all the machines that satisfied the
-            global filter. The second column (titled 'total') contains the total
-            number of tests that ran on that machine and satisfied the global
-            filter. The third column (titled 'good') contains the number of
-            those tests that passed on that machine.
-
-        filter_string: filter to apply to the common global filter to show the
-                       Table View drilldown of a histogram bucket
-
-        interval: interval for each bucket. E.g., 10 means that buckets should
-                  be 0-10%, 10%-20%, ...
-
-        """
-        self.query = query
-        self.filter_string = filter_string
-        self.interval = interval
-        self.drilldown_callback = drilldown_callback
-
-
-def _create_figure(height_inches):
-    """\
-    Creates an instance of matplotlib.figure.Figure, given the height in inches.
-    Returns the figure and the height in pixels.
-    """
-
-    fig = matplotlib.figure.Figure(
-        figsize=(_FIGURE_WIDTH_IN, height_inches + _FIGURE_BOTTOM_PADDING_IN),
-        dpi=_FIGURE_DPI, facecolor='white')
-    fig.subplots_adjust(bottom=float(_FIGURE_BOTTOM_PADDING_IN) / height_inches)
-    return (fig, fig.get_figheight() * _FIGURE_DPI)
-
-
-def _create_line(plots, labels, plot_info):
-    """\
-    Given all the data for the metrics, create a line plot.
-
-    plots: list of dicts containing the plot data. Each dict contains:
-            x: list of x-values for the plot
-            y: list of corresponding y-values
-            errors: errors for each data point, or None if no error information
-                    available
-            label: plot title
-    labels: list of x-tick labels
-    plot_info: a MetricsPlot
-    """
-    # when we're doing any kind of normalization, all series get put into a
-    # single plot
-    single = bool(plot_info.normalize_to)
-
-    area_data = []
-    lines = []
-    if single:
-        plot_height = _SINGLE_PLOT_HEIGHT
-    else:
-        plot_height = _MULTIPLE_PLOT_HEIGHT_PER_PLOT * len(plots)
-    figure, height = _create_figure(plot_height)
-
-    if single:
-        subplot = figure.add_subplot(1, 1, 1)
-
-    # Plot all the data
-    for plot_index, (plot, color) in enumerate(zip(plots, _colors(len(plots)))):
-        needs_invert = (plot['label'] in plot_info.inverted_series)
-
-        # Add a new subplot, if user wants multiple subplots
-        # Also handle axis inversion for subplots here
-        if not single:
-            subplot = figure.add_subplot(len(plots), 1, plot_index + 1)
-            subplot.set_title(plot['label'])
-            if needs_invert:
-                # for separate plots, just invert the y-axis
-                subplot.set_ylim(1, 0)
-        elif needs_invert:
-            # for a shared plot (normalized data), need to invert the y values
-            # manually, since all plots share a y-axis
-            plot['y'] = [-y for y in plot['y']]
-
-        # Plot the series
-        subplot.set_xticks(range(0, len(labels)))
-        subplot.set_xlim(-1, len(labels))
-        if single:
-            lines += subplot.plot(plot['x'], plot['y'], label=plot['label'],
-                                  marker=_MULTIPLE_PLOT_MARKER_TYPE,
-                                  markersize=_MULTIPLE_PLOT_MARKER_SIZE)
-            error_bar_color = lines[-1].get_color()
-        else:
-            lines += subplot.plot(plot['x'], plot['y'], _SINGLE_PLOT_STYLE,
-                                  label=plot['label'])
-            error_bar_color = _SINGLE_PLOT_ERROR_BAR_COLOR
-        if plot['errors']:
-            subplot.errorbar(plot['x'], plot['y'], linestyle='None',
-                             yerr=plot['errors'], color=error_bar_color)
-        subplot.set_xticklabels([])
-
-    # Construct the information for the drilldowns.
-    # We need to do this in a separate loop so that all the data is in
-    # matplotlib before we start calling transform(); otherwise, it will return
-    # incorrect data because it hasn't finished adjusting axis limits.
-    for line in lines:
-
-        # Get the pixel coordinates of each point on the figure
-        x = line.get_xdata()
-        y = line.get_ydata()
-        label = line.get_label()
-        icoords = line.get_transform().transform(zip(x,y))
-
-        # Get the appropriate drilldown query
-        drill = plot_info.query_dict['__' + label + '__']
-
-        # Set the title attributes (hover-over tool-tips)
-        x_labels = [labels[x_val] for x_val in x]
-        titles = ['%s - %s: %f' % (label, x_label, y_val)
-                  for x_label, y_val in zip(x_labels, y)]
-
-        # Get the appropriate parameters for the drilldown query
-        params = [dict(query=drill, series=line.get_label(), param=x_label)
-                  for x_label in x_labels]
-
-        area_data += [dict(left=ix - 5, top=height - iy - 5,
-                           right=ix + 5, bottom=height - iy + 5,
-                           title= title,
-                           callback=plot_info.drilldown_callback,
-                           callback_arguments=param_dict)
-                      for (ix, iy), title, param_dict
-                      in zip(icoords, titles, params)]
-
-    subplot.set_xticklabels(labels, rotation=90, size=_LINE_XTICK_LABELS_SIZE)
-
-    # Show the legend if there are not multiple subplots
-    if single:
-        font_properties = matplotlib.font_manager.FontProperties(
-            size=_LEGEND_FONT_SIZE)
-        legend = figure.legend(lines, [plot['label'] for plot in plots],
-                               prop=font_properties,
-                               handlelen=_LEGEND_HANDLE_LENGTH,
-                               numpoints=_LEGEND_NUM_POINTS)
-        # Workaround for matplotlib not keeping all line markers in the legend -
-        # it seems if we don't do this, matplotlib won't keep all the line
-        # markers in the legend.
-        for line in legend.get_lines():
-            line.set_marker(_LEGEND_MARKER_TYPE)
-
-    return (figure, area_data)
-
-
-def _get_adjusted_bar(x, bar_width, series_index, num_plots):
-    """\
-    Adjust the list 'x' to take the multiple series into account. Each series
-    should be shifted such that the middle series lies at the appropriate x-axis
-    tick with the other bars around it.  For example, if we had four series
-    (i.e. four bars per x value), we want to shift the left edges of the bars as
-    such:
-    Bar 1: -2 * width
-    Bar 2: -width
-    Bar 3: none
-    Bar 4: width
-    """
-    adjust = (-0.5 * num_plots - 1 + series_index) * bar_width
-    return [x_val + adjust for x_val in x]
-
-
-# TODO(showard): merge much of this function with _create_line by extracting and
-# parameterizing methods
-def _create_bar(plots, labels, plot_info):
-    """\
-    Given all the data for the metrics, create a line plot.
-
-    plots: list of dicts containing the plot data.
-            x: list of x-values for the plot
-            y: list of corresponding y-values
-            errors: errors for each data point, or None if no error information
-                    available
-            label: plot title
-    labels: list of x-tick labels
-    plot_info: a MetricsPlot
-    """
-
-    area_data = []
-    bars = []
-    figure, height = _create_figure(_SINGLE_PLOT_HEIGHT)
-
-    # Set up the plot
-    subplot = figure.add_subplot(1, 1, 1)
-    subplot.set_xticks(range(0, len(labels)))
-    subplot.set_xlim(-1, len(labels))
-    subplot.set_xticklabels(labels, rotation=90, size=_BAR_XTICK_LABELS_SIZE)
-    # draw a bold line at y=0, making it easier to tell if bars are dipping
-    # below the axis or not.
-    subplot.axhline(linewidth=2, color='black')
-
-    # width here is the width for each bar in the plot. Matplotlib default is
-    # 0.8.
-    width = 0.8 / len(plots)
-
-    # Plot the data
-    for plot_index, (plot, color) in enumerate(zip(plots, _colors(len(plots)))):
-        # Invert the y-axis if needed
-        if plot['label'] in plot_info.inverted_series:
-            plot['y'] = [-y for y in plot['y']]
-
-        adjusted_x = _get_adjusted_bar(plot['x'], width, plot_index + 1,
-                                       len(plots))
-        bar_data = subplot.bar(adjusted_x, plot['y'],
-                               width=width, yerr=plot['errors'],
-                               facecolor=color,
-                               label=plot['label'])
-        bars.append(bar_data[0])
-
-    # Construct the information for the drilldowns.
-    # See comment in _create_line for why we need a separate loop to do this.
-    for plot_index, plot in enumerate(plots):
-        adjusted_x = _get_adjusted_bar(plot['x'], width, plot_index + 1,
-                                       len(plots))
-
-        # Let matplotlib plot the data, so that we can get the data-to-image
-        # coordinate transforms
-        line = subplot.plot(adjusted_x, plot['y'], linestyle='None')[0]
-        label = plot['label']
-        upper_left_coords = line.get_transform().transform(zip(adjusted_x,
-                                                               plot['y']))
-        bottom_right_coords = line.get_transform().transform(
-            [(x + width, 0) for x in adjusted_x])
-
-        # Get the drilldown query
-        drill = plot_info.query_dict['__' + label + '__']
-
-        # Set the title attributes
-        x_labels = [labels[x] for x in plot['x']]
-        titles = ['%s - %s: %f' % (plot['label'], label, y)
-                  for label, y in zip(x_labels, plot['y'])]
-        params = [dict(query=drill, series=plot['label'], param=x_label)
-                  for x_label in x_labels]
-        area_data += [dict(left=ulx, top=height - uly,
-                           right=brx, bottom=height - bry,
-                           title=title,
-                           callback=plot_info.drilldown_callback,
-                           callback_arguments=param_dict)
-                      for (ulx, uly), (brx, bry), title, param_dict
-                      in zip(upper_left_coords, bottom_right_coords, titles,
-                             params)]
-
-    figure.legend(bars, [plot['label'] for plot in plots])
-    return (figure, area_data)
-
-
-def _normalize(data_values, data_errors, base_values, base_errors):
-    """\
-    Normalize the data against a baseline.
-
-    data_values: y-values for the to-be-normalized data
-    data_errors: standard deviations for the to-be-normalized data
-    base_values: list of values normalize against
-    base_errors: list of standard deviations for those base values
-    """
-    values = []
-    for value, base in zip(data_values, base_values):
-        try:
-            values.append(100 * (value - base) / base)
-        except ZeroDivisionError:
-            # Base is 0.0 so just simplify:
-            #   If value < base: append -100.0;
-            #   If value == base: append 0.0 (obvious); and
-            #   If value > base: append 100.0.
-            values.append(100 * float(cmp(value, base)))
-
-    # Based on error for f(x,y) = 100 * (x - y) / y
-    if data_errors:
-        if not base_errors:
-            base_errors = [0] * len(data_errors)
-        errors = []
-        for data, error, base_value, base_error in zip(
-                data_values, data_errors, base_values, base_errors):
-            try:
-                errors.append(sqrt(error**2 * (100 / base_value)**2
-                        + base_error**2 * (100 * data / base_value**2)**2
-                        + error * base_error * (100 / base_value**2)**2))
-            except ZeroDivisionError:
-                # Again, base is 0.0 so do the simple thing.
-                errors.append(100 * abs(error))
-    else:
-        errors = None
-
-    return (values, errors)
-
-
-def _create_png(figure):
-    """\
-    Given the matplotlib figure, generate the PNG data for it.
-    """
-
-    # Draw the image
-    canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(figure)
-    canvas.draw()
-    size = canvas.get_renderer().get_canvas_width_height()
-    image_as_string = canvas.tostring_rgb()
-    image = PIL.Image.fromstring('RGB', size, image_as_string, 'raw', 'RGB', 0,
-                                 1)
-    image_background = PIL.Image.new(image.mode, image.size,
-                                     figure.get_facecolor())
-
-    # Crop the image to remove surrounding whitespace
-    non_whitespace = PIL.ImageChops.difference(image, image_background)
-    bounding_box = non_whitespace.getbbox()
-    image = image.crop(bounding_box)
-
-    image_data = StringIO.StringIO()
-    image.save(image_data, format='PNG')
-
-    return image_data.getvalue(), bounding_box
-
-
-def _create_image_html(figure, area_data, plot_info):
-    """\
-    Given the figure and drilldown data, construct the HTML that will render the
-    graph as a PNG image, and attach the image map to that image.
-
-    figure: figure containing the drawn plot(s)
-    area_data: list of parameters for each area of the image map. See the
-               definition of the template string '_AREA_TEMPLATE'
-    plot_info: a MetricsPlot or QualHistogram
-    """
-
-    png, bbox = _create_png(figure)
-
-    # Construct the list of image map areas
-    areas = [_AREA_TEMPLATE %
-             (data['left'] - bbox[0], data['top'] - bbox[1],
-              data['right'] - bbox[0], data['bottom'] - bbox[1],
-              data['title'], data['callback'],
-              _json_encoder.encode(data['callback_arguments'])
-                  .replace('"', '&quot;'))
-             for data in area_data]
-
-    map_name = plot_info.drilldown_callback + '_map'
-    return _HTML_TEMPLATE % (base64.b64encode(png), map_name, map_name,
-                             '\n'.join(areas))
-
-
-def _find_plot_by_label(plots, label):
-    for index, plot in enumerate(plots):
-        if plot['label'] == label:
-            return index
-    raise ValueError('no plot labeled "%s" found' % label)
-
-
-def _normalize_to_series(plots, base_series):
-    base_series_index = _find_plot_by_label(plots, base_series)
-    base_plot = plots[base_series_index]
-    base_xs = base_plot['x']
-    base_values = base_plot['y']
-    base_errors = base_plot['errors']
-    del plots[base_series_index]
-
-    for plot in plots:
-        old_xs, old_values, old_errors = plot['x'], plot['y'], plot['errors']
-        new_xs, new_values, new_errors = [], [], []
-        new_base_values, new_base_errors = [], []
-        # Select only points in the to-be-normalized data that have a
-        # corresponding baseline value
-        for index, x_value in enumerate(old_xs):
-            try:
-                base_index = base_xs.index(x_value)
-            except ValueError:
-                continue
-
-            new_xs.append(x_value)
-            new_values.append(old_values[index])
-            new_base_values.append(base_values[base_index])
-            if old_errors:
-                new_errors.append(old_errors[index])
-                new_base_errors.append(base_errors[base_index])
-
-        if not new_xs:
-            raise NoDataError('No normalizable data for series ' +
-                              plot['label'])
-        plot['x'] = new_xs
-        plot['y'] = new_values
-        if old_errors:
-            plot['errors'] = new_errors
-
-        plot['y'], plot['errors'] = _normalize(plot['y'], plot['errors'],
-                                               new_base_values,
-                                               new_base_errors)
-
-
-def _create_metrics_plot_helper(plot_info, extra_text=None):
-    """
-    Create a metrics plot of the given plot data.
-    plot_info: a MetricsPlot object.
-    extra_text: text to show at the uppper-left of the graph
-
-    TODO(showard): move some/all of this logic into methods on MetricsPlot
-    """
-    query = plot_info.query_dict['__main__']
-    cursor = readonly_connection.cursor()
-    cursor.execute(query)
-
-    if not cursor.rowcount:
-        raise NoDataError('query did not return any data')
-    rows = cursor.fetchall()
-    # "transpose" rows, so columns[0] is all the values from the first column,
-    # etc.
-    columns = zip(*rows)
-
-    plots = []
-    labels = [str(label) for label in columns[0]]
-    needs_resort = (cursor.description[0][0] == 'kernel')
-
-    # Collect all the data for the plot
-    col = 1
-    while col < len(cursor.description):
-        y = columns[col]
-        label = cursor.description[col][0]
-        col += 1
-        if (col < len(cursor.description) and
-            'errors-' + label == cursor.description[col][0]):
-            errors = columns[col]
-            col += 1
-        else:
-            errors = None
-        if needs_resort:
-            y = _resort(labels, y)
-            if errors:
-                errors = _resort(labels, errors)
-
-        x = [index for index, value in enumerate(y) if value is not None]
-        if not x:
-            raise NoDataError('No data for series ' + label)
-        y = [y[i] for i in x]
-        if errors:
-            errors = [errors[i] for i in x]
-        plots.append({
-            'label': label,
-            'x': x,
-            'y': y,
-            'errors': errors
-        })
-
-    if needs_resort:
-        labels = _resort(labels, labels)
-
-    # Normalize the data if necessary
-    normalize_to = plot_info.normalize_to
-    if normalize_to == 'first' or normalize_to.startswith('x__'):
-        if normalize_to != 'first':
-            baseline = normalize_to[3:]
-            try:
-                baseline_index = labels.index(baseline)
-            except ValueError:
-                raise ValidationError({
-                    'Normalize' : 'Invalid baseline %s' % baseline
-                    })
-        for plot in plots:
-            if normalize_to == 'first':
-                plot_index = 0
-            else:
-                try:
-                    plot_index = plot['x'].index(baseline_index)
-                # if the value is not found, then we cannot normalize
-                except ValueError:
-                    raise ValidationError({
-                        'Normalize' : ('%s does not have a value for %s'
-                                       % (plot['label'], normalize_to[3:]))
-                        })
-            base_values = [plot['y'][plot_index]] * len(plot['y'])
-            if plot['errors']:
-                base_errors = [plot['errors'][plot_index]] * len(plot['errors'])
-            plot['y'], plot['errors'] = _normalize(plot['y'], plot['errors'],
-                                                   base_values,
-                                                   None or base_errors)
-
-    elif normalize_to.startswith('series__'):
-        base_series = normalize_to[8:]
-        _normalize_to_series(plots, base_series)
-
-    # Call the appropriate function to draw the line or bar plot
-    if plot_info.is_line:
-        figure, area_data = _create_line(plots, labels, plot_info)
-    else:
-        figure, area_data = _create_bar(plots, labels, plot_info)
-
-    # TODO(showard): extract these magic numbers to named constants
-    if extra_text:
-        text_y = .95 - .0075 * len(plots)
-        figure.text(.1, text_y, extra_text, size='xx-small')
-
-    return (figure, area_data)
-
-
-def create_metrics_plot(query_dict, plot_type, inverted_series, normalize_to,
-                        drilldown_callback, extra_text=None):
-    plot_info = MetricsPlot(query_dict, plot_type, inverted_series,
-                            normalize_to, drilldown_callback)
-    figure, area_data = _create_metrics_plot_helper(plot_info, extra_text)
-    return _create_image_html(figure, area_data, plot_info)
-
-
-def _get_hostnames_in_bucket(hist_data, bucket):
-    """\
-    Get all the hostnames that constitute a particular bucket in the histogram.
-
-    hist_data: list containing tuples of (hostname, pass_rate)
-    bucket: tuple containing the (low, high) values of the target bucket
-    """
-
-    return [hostname for hostname, pass_rate in hist_data
-            if bucket[0] <= pass_rate < bucket[1]]
-
-
-def _create_qual_histogram_helper(plot_info, extra_text=None):
-    """\
-    Create a machine qualification histogram of the given data.
-
-    plot_info: a QualificationHistogram
-    extra_text: text to show at the upper-left of the graph
-
-    TODO(showard): move much or all of this into methods on
-    QualificationHistogram
-    """
-    cursor = readonly_connection.cursor()
-    cursor.execute(plot_info.query)
-
-    if not cursor.rowcount:
-        raise NoDataError('query did not return any data')
-
-    # Lists to store the plot data.
-    # hist_data store tuples of (hostname, pass_rate) for machines that have
-    #     pass rates between 0 and 100%, exclusive.
-    # no_tests is a list of machines that have run none of the selected tests
-    # no_pass is a list of machines with 0% pass rate
-    # perfect is a list of machines with a 100% pass rate
-    hist_data = []
-    no_tests = []
-    no_pass = []
-    perfect = []
-
-    # Construct the lists of data to plot
-    for hostname, total, good in cursor.fetchall():
-        if total == 0:
-            no_tests.append(hostname)
-            continue
-
-        if good == 0:
-            no_pass.append(hostname)
-        elif good == total:
-            perfect.append(hostname)
-        else:
-            percentage = 100.0 * good / total
-            hist_data.append((hostname, percentage))
-
-    interval = plot_info.interval
-    bins = range(0, 100, interval)
-    if bins[-1] != 100:
-        bins.append(bins[-1] + interval)
-
-    figure, height = _create_figure(_SINGLE_PLOT_HEIGHT)
-    subplot = figure.add_subplot(1, 1, 1)
-
-    # Plot the data and get all the bars plotted
-    _,_, bars = subplot.hist([data[1] for data in hist_data],
-                         bins=bins, align='left')
-    bars += subplot.bar([-interval], len(no_pass),
-                    width=interval, align='center')
-    bars += subplot.bar([bins[-1]], len(perfect),
-                    width=interval, align='center')
-    bars += subplot.bar([-3 * interval], len(no_tests),
-                    width=interval, align='center')
-
-    buckets = [(bin, min(bin + interval, 100)) for bin in bins[:-1]]
-    # set the x-axis range to cover all the normal bins plus the three "special"
-    # ones - N/A (3 intervals left), 0% (1 interval left) ,and 100% (far right)
-    subplot.set_xlim(-4 * interval, bins[-1] + interval)
-    subplot.set_xticks([-3 * interval, -interval] + bins + [100 + interval])
-    subplot.set_xticklabels(['N/A', '0%'] +
-                        ['%d%% - <%d%%' % bucket for bucket in buckets] +
-                        ['100%'], rotation=90, size='small')
-
-    # Find the coordinates on the image for each bar
-    x = []
-    y = []
-    for bar in bars:
-        x.append(bar.get_x())
-        y.append(bar.get_height())
-    f = subplot.plot(x, y, linestyle='None')[0]
-    upper_left_coords = f.get_transform().transform(zip(x, y))
-    bottom_right_coords = f.get_transform().transform(
-        [(x_val + interval, 0) for x_val in x])
-
-    # Set the title attributes
-    titles = ['%d%% - <%d%%: %d machines' % (bucket[0], bucket[1], y_val)
-              for bucket, y_val in zip(buckets, y)]
-    titles.append('0%%: %d machines' % len(no_pass))
-    titles.append('100%%: %d machines' % len(perfect))
-    titles.append('N/A: %d machines' % len(no_tests))
-
-    # Get the hostnames for each bucket in the histogram
-    names_list = [_get_hostnames_in_bucket(hist_data, bucket)
-                  for bucket in buckets]
-    names_list += [no_pass, perfect]
-
-    if plot_info.filter_string:
-        plot_info.filter_string += ' AND '
-
-    # Construct the list of drilldown parameters to be passed when the user
-    # clicks on the bar.
-    params = []
-    for names in names_list:
-        if names:
-            hostnames = ','.join(_quote(hostname) for hostname in names)
-            hostname_filter = 'hostname IN (%s)' % hostnames
-            full_filter = plot_info.filter_string + hostname_filter
-            params.append({'type': 'normal',
-                           'filterString': full_filter})
-        else:
-            params.append({'type': 'empty'})
-
-    params.append({'type': 'not_applicable',
-                   'hosts': '<br />'.join(no_tests)})
-
-    area_data = [dict(left=ulx, top=height - uly,
-                      right=brx, bottom=height - bry,
-                      title=title, callback=plot_info.drilldown_callback,
-                      callback_arguments=param_dict)
-                 for (ulx, uly), (brx, bry), title, param_dict
-                 in zip(upper_left_coords, bottom_right_coords, titles, params)]
-
-    # TODO(showard): extract these magic numbers to named constants
-    if extra_text:
-        figure.text(.1, .95, extra_text, size='xx-small')
-
-    return (figure, area_data)
-
-
-def create_qual_histogram(query, filter_string, interval, drilldown_callback,
-                          extra_text=None):
-    plot_info = QualificationHistogram(query, filter_string, interval,
-                                       drilldown_callback)
-    figure, area_data = _create_qual_histogram_helper(plot_info, extra_text)
-    return _create_image_html(figure, area_data, plot_info)
-
-
-def create_embedded_plot(model, update_time):
-    """\
-    Given an EmbeddedGraphingQuery object, generate the PNG image for it.
-
-    model: EmbeddedGraphingQuery object
-    update_time: 'Last updated' time
-    """
-
-    params = pickle.loads(model.params)
-    extra_text = 'Last updated: %s' % update_time
-
-    if model.graph_type == 'metrics':
-        plot_info = MetricsPlot(query_dict=params['queries'],
-                                plot_type=params['plot'],
-                                inverted_series=params['invert'],
-                                normalize_to=None,
-                                drilldown_callback='')
-        figure, areas_unused = _create_metrics_plot_helper(plot_info,
-                                                           extra_text)
-    elif model.graph_type == 'qual':
-        plot_info = QualificationHistogram(
-            query=params['query'], filter_string=params['filter_string'],
-            interval=params['interval'], drilldown_callback='')
-        figure, areas_unused = _create_qual_histogram_helper(plot_info,
-                                                             extra_text)
-    else:
-        raise ValueError('Invalid graph_type %s' % model.graph_type)
-
-    image, bounding_box_unused = _create_png(figure)
-    return image
-
-
-_cache_timeout = global_config.global_config.get_config_value(
-    'AUTOTEST_WEB', 'graph_cache_creation_timeout_minutes')
-
-
-def handle_plot_request(id, max_age):
-    """\
-    Given the embedding id of a graph, generate a PNG of the embedded graph
-    associated with that id.
-
-    id: id of the embedded graph
-    max_age: maximum age, in minutes, that a cached version should be held
-    """
-    model = models.EmbeddedGraphingQuery.objects.get(id=id)
-
-    # Check if the cached image needs to be updated
-    now = datetime.datetime.now()
-    update_time = model.last_updated + datetime.timedelta(minutes=int(max_age))
-    if now > update_time:
-        cursor = django.db.connection.cursor()
-
-        # We want this query to update the refresh_time only once, even if
-        # multiple threads are running it at the same time. That is, only the
-        # first thread will win the race, and it will be the one to update the
-        # cached image; all other threads will show that they updated 0 rows
-        query = """
-            UPDATE embedded_graphing_queries
-            SET refresh_time = NOW()
-            WHERE id = %s AND (
-                refresh_time IS NULL OR
-                refresh_time + INTERVAL %s MINUTE < NOW()
-            )
-        """
-        cursor.execute(query, (id, _cache_timeout))
-
-        # Only refresh the cached image if we were successful in updating the
-        # refresh time
-        if cursor.rowcount:
-            model.cached_png = create_embedded_plot(model, now.ctime())
-            model.last_updated = now
-            model.refresh_time = None
-            model.save()
-
-    return model.cached_png
diff --git a/frontend/tko/models.py b/frontend/tko/models.py
index 81e2928..de0c3ae 100644
--- a/frontend/tko/models.py
+++ b/frontend/tko/models.py
@@ -309,25 +309,6 @@
         db_table = 'tko_saved_queries'
 
 
-class EmbeddedGraphingQuery(dbmodels.Model, model_logic.ModelExtensions):
-    """Models an embedded graphing query."""
-    url_token = dbmodels.TextField(null=False, blank=False)
-    graph_type = dbmodels.CharField(max_length=16, null=False, blank=False)
-    params = dbmodels.TextField(null=False, blank=False)
-    last_updated = dbmodels.DateTimeField(null=False, blank=False,
-                                          editable=False)
-    # refresh_time shows the time at which a thread is updating the cached
-    # image, or NULL if no one is updating the image. This is used so that only
-    # one thread is updating the cached image at a time (see
-    # graphing_utils.handle_plot_request).
-    refresh_time = dbmodels.DateTimeField(editable=False)
-    cached_png = dbmodels.TextField(editable=False)
-
-    class Meta:
-        """Metadata for class EmbeddedGraphingQuery."""
-        db_table = 'tko_embedded_graphing_queries'
-
-
 # Views.
 
 class TestViewManager(TempManager):
diff --git a/frontend/tko/views.py b/frontend/tko/views.py
index 2b29c9f..2ea64b7 100644
--- a/frontend/tko/views.py
+++ b/frontend/tko/views.py
@@ -1,7 +1,6 @@
-import django.http
-from autotest_lib.frontend.tko import rpc_interface, graphing_utils
+from autotest_lib.frontend.tko import rpc_interface
 from autotest_lib.frontend.tko import csv_encoder
-from autotest_lib.frontend.afe import rpc_handler, rpc_utils
+from autotest_lib.frontend.afe import rpc_handler
 
 rpc_handler_obj = rpc_handler.RpcHandler((rpc_interface,),
                                          document_module=rpc_interface)
@@ -27,7 +26,4 @@
 
 
 def handle_plot(request):
-    id = request.GET['id']
-    max_age = request.GET['max_age']
-    return django.http.HttpResponse(
-        graphing_utils.handle_plot_request(id, max_age), mimetype='image/png')
+    raise DeprecationWarning()