From 6d034bab697198e7c91cd9b52d94db66ece4da16 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 4 Nov 2020 16:35:53 +0000 Subject: [PATCH] Add ability to upload files --- Api/KernelFunctions.cs | 20 +++++++++++++ Client/Pages/Notebook/Cells.razor | 10 +++++-- Client/Pages/Notebook/FileCell.razor | 25 ++++++++++++++++ Client/Pages/Notebook/FileCell.razor.css | 14 +++++++++ Client/Pages/Notebook/NewCell.razor | 38 +++++++++++++++++++++--- Shared/Cell.cs | 3 +- 6 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 Client/Pages/Notebook/FileCell.razor create mode 100644 Client/Pages/Notebook/FileCell.razor.css diff --git a/Api/KernelFunctions.cs b/Api/KernelFunctions.cs index 4b5b001..f667964 100644 --- a/Api/KernelFunctions.cs +++ b/Api/KernelFunctions.cs @@ -93,6 +93,26 @@ public async Task GetCompletions( return new OkObjectResult(result); } + [FunctionName("UploadFile")] + public async Task UploadFile( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "code/uploadfile")] HttpRequest req, + ILogger log) + { + using var requestData = new MemoryStream(); + await req.Body.CopyToAsync(requestData); + + // TODO: Is there a way to copy data into the kernel without stringification? + var requestDataBase64 = Convert.ToBase64String(new Span(requestData.GetBuffer(), 0, (int)requestData.Length)); + + var notebookId = req.Query["notebookId"].First(); + var variable = req.Query["variable"].First(); + var kernel = _kernels.GetKernelForNotebook(notebookId); + + var code = $"var {variable} = Convert.FromBase64String(\"{requestDataBase64}\");"; + var request = await kernel.SendAsync(new SubmitCode(code), CancellationToken.None); + return new OkResult(); + } + // TODO: Avoid duplication by changing MonacoRazor to target 3.0 or moving shared types to seperate package. public enum CompletionItemKind : int { diff --git a/Client/Pages/Notebook/Cells.razor b/Client/Pages/Notebook/Cells.razor index 45539ce..9cbdd7a 100644 --- a/Client/Pages/Notebook/Cells.razor +++ b/Client/Pages/Notebook/Cells.razor @@ -9,14 +9,18 @@ var cell = notebook.Cells[index]; if (cell.Type == CellType.Code) { - + + } + else if (cell.Type == CellType.File) + { + } else { - + } - + } } diff --git a/Client/Pages/Notebook/FileCell.razor b/Client/Pages/Notebook/FileCell.razor new file mode 100644 index 0000000..ad01212 --- /dev/null +++ b/Client/Pages/Notebook/FileCell.razor @@ -0,0 +1,25 @@ +@inject HttpClient Http + +
+ @Cell.Content: + +
@status
+
+ +@code { + [Parameter] + public blazoract.Shared.Cell Cell { get; set; } + + private string status; + + private async Task SupplyFileAsync(InputFileChangeEventArgs eventArgs) + { + using var fileStream = eventArgs.File.OpenReadStream(maxAllowedSize: 5*1024*1024); + + status = $"Sending {eventArgs.File.Size} bytes..."; + var url = $"api/code/uploadfile?notebookId={Cell.NotebookId}&variable={Cell.Content}"; + await Http.PostAsync(url, new StreamContent(fileStream)); + + status = $"Finished sending {eventArgs.File.Size} bytes."; + } +} diff --git a/Client/Pages/Notebook/FileCell.razor.css b/Client/Pages/Notebook/FileCell.razor.css new file mode 100644 index 0000000..4182051 --- /dev/null +++ b/Client/Pages/Notebook/FileCell.razor.css @@ -0,0 +1,14 @@ +.file { + display: table; + background-color: #eee; + padding: 1em 2em; + margin: 1em auto; +} + +span { + font-family: monospace; +} + +::deep input[type=file] { + margin-left: 3em; +} diff --git a/Client/Pages/Notebook/NewCell.razor b/Client/Pages/Notebook/NewCell.razor index d760bf6..97a9757 100644 --- a/Client/Pages/Notebook/NewCell.razor +++ b/Client/Pages/Notebook/NewCell.razor @@ -15,6 +15,16 @@ + @code { @@ -22,15 +32,35 @@ public int Position { get; set; } [Parameter] - public string NotebookId { get; set; } + public blazoract.Shared.Notebook Notebook { get; set; } public async Task AddCodeCell() { - await content.AddCell(NotebookId, "", CellType.Code, Position + 1); + await content.AddCell(Notebook.NotebookId, "", CellType.Code, Position + 1); } public async Task AddTextCell() { - await content.AddCell(NotebookId, "Add text here...", CellType.Text, Position + 1); + await content.AddCell(Notebook.NotebookId, "Add text here...", CellType.Text, Position + 1); + } + + public async Task AddFileCell() + { + var fileIndex = ChooseAvailableFileVariableName(); + await content.AddCell(Notebook.NotebookId, fileIndex, CellType.File, Position + 1); + } + + private string ChooseAvailableFileVariableName() + { + var existingFileVariables = Notebook.Cells.Where(c => c.Type == CellType.File).ToDictionary(c => c.Content, c => c); + + for (var i = 1; ; i++) + { + var candidateName = $"file{i}"; + if (!existingFileVariables.ContainsKey(candidateName)) + { + return candidateName; + } + } } -} \ No newline at end of file +} diff --git a/Shared/Cell.cs b/Shared/Cell.cs index 64311ec..026a404 100644 --- a/Shared/Cell.cs +++ b/Shared/Cell.cs @@ -21,6 +21,7 @@ public Cell(string notebookId, string content, CellType type = CellType.Code) public enum CellType { Code, - Text + Text, + File, } }