From a12586eaefeaeb0776e935a28d44a1793d13ee52 Mon Sep 17 00:00:00 2001 From: Roger-Luo Date: Tue, 13 Jul 2021 00:56:43 -0400 Subject: [PATCH] menu roughly works --- Project.toml | 2 + src/AWSBraket.jl | 7 ++- src/menu.jl | 129 +++++++++++++++++++++++++++++++++++++++++++ src/schema/device.jl | 2 +- test/menu.jl | 30 ++++++++++ 5 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 src/menu.jl create mode 100644 test/menu.jl diff --git a/Project.toml b/Project.toml index 1397028..f87bd33 100644 --- a/Project.toml +++ b/Project.toml @@ -6,12 +6,14 @@ version = "0.1.0" [deps] AWS = "fbe9abb3-538b-5e4e-ba9e-bc94f4f92ebc" Configurations = "5218b696-f38b-4ac9-8b61-a12ec717816d" +Crayons = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Expronicon = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636" ExproniconLite = "55351af7-c7e9-48d6-89ff-24e801d99491" GarishPrint = "b0ab02a7-8576-43f7-aa76-eaa7c3897c54" HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [compat] diff --git a/src/AWSBraket.jl b/src/AWSBraket.jl index ef3e92d..9096610 100644 --- a/src/AWSBraket.jl +++ b/src/AWSBraket.jl @@ -10,6 +10,7 @@ using AWS using UUIDs using HTTP using JSON +using Configurations: from_dict_validate using AWS: @service @service STS @@ -21,7 +22,7 @@ function parse_device_info(d) d["deviceCapabilities"] = JSON.parse(d["deviceCapabilities"]) end - return from_dict(Schema.DeviceInfo, d)::Schema.DeviceInfo + return from_dict_validate(Schema.DeviceInfo, d)::Schema.DeviceInfo end function get_device(arn::String; aws_config=AWS.global_aws_config()) @@ -97,4 +98,8 @@ function cancel_quantum_task(client_token::String, task_arn::String) Braket.cancel_quantum_task(client_token, HTTP.escapeuri(task_arn)) end +using Crayons.Box +using REPL.TerminalMenus +include("menu.jl") + end diff --git a/src/menu.jl b/src/menu.jl new file mode 100644 index 0000000..559c8f1 --- /dev/null +++ b/src/menu.jl @@ -0,0 +1,129 @@ +mutable struct DeviceMenu <: TerminalMenus.AbstractMenu + options::Vector{Schema.DeviceInfo} + regions::Vector{String} + pagesize::Int + indent::Int + pageoffset::Int + selected::Int +end + +function DeviceMenu(; pagesize::Int=5) + all_devices = Schema.DeviceInfo[] + regions = String[] + for region in ["us-east-1", "us-west-1", "us-west-2"] + devices, _ = search_devices(;aws_config = AWSConfig(;region)) + append!(all_devices, devices) + append!(regions, fill(region, length(devices))) + end + return DeviceMenu(all_devices, regions; pagesize) +end + +function DeviceMenu( + devices::Vector{Schema.DeviceInfo}, + regions::Vector{String} = fill(AWS.region(AWS.global_aws_config()), length(devices)) + ; pagesize::Int=5, warn::Bool=true + ) + + length(devices) < 1 && error("DeviceMenu must have at least one option") + pagesize < 4 && error("minimum pagesize must be larger than 4") + pagesize = pagesize == -1 ? length(devices) : pagesize + # pagesize shouldn't be bigger than options + pagesize = min(length(devices), pagesize) + # after other checks, pagesize must be greater than 1 + pagesize < 1 && error("pagesize must be >= 1") + + pageoffset = 0 + selected = -1 # none + indent = maximum(x->length(device_name(x)), devices) + 2 + return DeviceMenu(devices, regions, pagesize, indent, pageoffset, selected) +end + +function device_name(dev::Schema.DeviceInfo) + string(dev.providerName, ":", dev.deviceName) +end + +function TerminalMenus.options(m::DeviceMenu) + return m.options +end + +TerminalMenus.cancel(m::DeviceMenu) = m.selected = -1 + +function TerminalMenus.pick(m::DeviceMenu, cursor::Int) + m.selected = cursor + return true #break out of the menu +end + +function TerminalMenus.printmenu(out::IO, m::DeviceMenu, cursoridx::Int; oldstate=nothing, init::Bool=false) + buf = IOBuffer() + lastoption = length(m.options) + ncleared = oldstate === nothing ? m.pagesize-1 : oldstate + + if init + # like clamp, except this takes the min if max < min + m.pageoffset = max(0, min(cursoridx - m.pagesize ÷ 2, lastoption - m.pagesize)) + else + print(buf, "\x1b[999D\x1b[$(ncleared)A") # move left 999 spaces and up `ncleared` lines + end + + firstline = m.pageoffset+1 + lastline = min(m.pagesize+m.pageoffset, lastoption) + curr_device = m.options[cursoridx] + + for i in firstline:lastline + # clearline + print(buf, "\x1b[2K") + + upscrollable = i == firstline && m.pageoffset > 0 + downscrollable = i == lastline && i != lastoption + + if upscrollable && downscrollable + print(buf, TerminalMenus.updown_arrow(m)::Union{Char,String}) + elseif upscrollable + print(buf, TerminalMenus.up_arrow(m)::Union{Char,String}) + elseif downscrollable + print(buf, TerminalMenus.down_arrow(m)::Union{Char,String}) + else + print(buf, ' ') + end + + # TODO: use 1.6's implementation when we drop 1.5 + # TerminalMenus.printcursor(buf, m, i == cursoridx) + print(buf, i == cursoridx ? '→' : ' ', ' ') + + name = device_name(m.options[i]) + indent = " "^(m.indent - length(name)) + device = m.options[cursoridx]::Schema.DeviceInfo + + line_idx = i - firstline + 1 + print(buf, GREEN_FG(name)) + + if !isnothing(device.deviceCapabilities) && !isnothing(device.deviceCapabilities.paradigm) + nqubits = device.deviceCapabilities.paradigm.qubitCount + else + nqubits = "unknown" + end + + if line_idx == 1 + print(buf, indent, LIGHT_BLUE_FG("nqubits: "), GREEN_FG(string(nqubits))) + elseif line_idx == 2 + print(buf, indent, LIGHT_BLUE_FG("status: "), GREEN_FG(device.deviceStatus)) + elseif line_idx == 3 + print(buf, indent, LIGHT_BLUE_FG("region: "), GREEN_FG(m.regions[cursoridx])) + end + + (firstline == lastline || i != lastline) && print(buf, "\r\n") + end + + newstate = lastline-firstline # final line doesn't have `\n` + if newstate < ncleared && oldstate !== nothing + # we printed fewer lines than last time. Erase the leftovers. + for i = newstate+1:ncleared + print(buf, "\r\n\x1b[2K") + end + print(buf, "\x1b[$(ncleared-newstate)A") + end + + print(out, String(take!(buf))) + + return newstate +end diff --git a/src/schema/device.jl b/src/schema/device.jl index 331a70f..7960e59 100644 --- a/src/schema/device.jl +++ b/src/schema/device.jl @@ -268,7 +268,7 @@ end deviceName::String deviceType::String deviceStatus::String - deviceCapabilities::DeviceCapabilities + deviceCapabilities::Maybe{DeviceCapabilities} = nothing end function Configurations.convert_to_option(::Type{BraketDeviceSchema}, ::Type{VersionNumber}, x) diff --git a/test/menu.jl b/test/menu.jl new file mode 100644 index 0000000..7f1c116 --- /dev/null +++ b/test/menu.jl @@ -0,0 +1,30 @@ +using AWS +using AWSBraket +using REPL.TerminalMenus + + +devices, _ = AWSBraket.search_devices(;aws_config=AWSConfig(;region="us-east-1")) + +menu = AWSBraket.DeviceMenu(devices); + +menu = AWSBraket.DeviceMenu() +request("select a device:", menu) + + + +devices, _ = AWSBraket.search_devices(;aws_config=AWSConfig(;region="us-west-2")) + +AWS.@service Braket + +devices = Braket.search_devices([];aws_config=AWSConfig(;region="us-west-2"))["devices"] + +using JSON +using AWSBraket.Schema +using Configurations + +from_dict(Schema.DeviceInfo, devices[4]) + +devices[1]["deviceCapabilities"] = JSON.parse(devices[1]["deviceCapabilities"]) + +AWSBraket.parse_device_info(devices[4]) +devices[4] \ No newline at end of file