Start new IPython Notebook daemon.

        :param name: The owner of the Notebook will be *name*. He/she gets a new Notebook content folder created where all files are placed.

        :param context: Extra context information passed to the started Notebook. This must contain {context_hash:int} parameter used to identify the launch parameters for the notebook

def start_notebook(self, name, context: dict, fg=False):
        """Start new IPython Notebook daemon.

        :param name: The owner of the Notebook will be *name*. He/she gets a new Notebook content folder created where all files are placed.

        :param context: Extra context information passed to the started Notebook. This must contain {context_hash:int} parameter used to identify the launch parameters for the notebook
        """
        assert context
        assert type(context) == dict
        assert "context_hash" in context
        assert type(context["context_hash"]) == int

        http_port = self.pick_port()
        assert http_port
        context = context.copy()
        context["http_port"] = http_port

        # We can't proxy websocket URLs, so let them go directly through localhost or have front end server to do proxying (nginx)
        if "websocket_url" not in context:
            context["websocket_url"] = "ws://localhost:{port}".format(port=http_port)

        if "{port}" in context["websocket_url"]:
            # Do port substitution for the websocket URL
            context["websocket_url"] = context["websocket_url"].format(port=http_port)

        pid = self.get_pid(name)
        assert "terminated" not in context

        comm.set_context(pid, context)

        if fg:
            self.exec_notebook_daemon_command(name, "fg", port=http_port)
        else:
            self.exec_notebook_daemon_command(name, "start", port=http_port)