Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 71 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,85 @@ Suppose that the `Show API request` button generated the following Python code:
```python
#!/usr/bin/env python
import cdsapi
c = cdsapi.Client()
c.retrieve("insitu-glaciers-elevation-mass",
{
"variable": "all",
"product_type": "elevation_change",
"file_version": "20170405",
"format": "tgz"
},
"download.tar.gz")

dataset = "reanalysis-era5-single-levels"
request = {
"product_type": ["reanalysis"],
"variable": [
"10m_u_component_of_wind",
"10m_v_component_of_wind"
],
"year": ["2024"],
"month": ["12"],
"day": ["06"],
"time": ["16:00"],
"data_format": "netcdf",
"download_format": "unarchived",
"area": [58, 6, 55, 9]
}

client = cdsapi.Client()
client.retrieve(dataset, request).download()
```

You can obtain the same results in Julia with the following code:
You can obtain the same results in Julia by simply copying the request python dict and wrap it into a multiline string:

```julia
using CDSAPI

CDSAPI.retrieve("insitu-glaciers-elevation-mass",
CDSAPI.py2ju("""
{
"variable": "all",
"product_type": "elevation_change",
"file_version": "20170405",
"format": "tgz"
}
"""),
"download.tar.gz")
dataset = "reanalysis-era5-single-levels"
request = """{
"product_type": ["reanalysis"],
"variable": [
"10m_u_component_of_wind",
"10m_v_component_of_wind"
],
"year": ["2024"],
"month": ["12"],
"day": ["06"],
"time": ["16:00"],
"data_format": "netcdf",
"download_format": "unarchived",
"area": [58, 6, 55, 9]
}""" #<- notice the multiline string.

CDSAPI.retrieve(dataset, request, "download.nc")
```
We've copied/pasted the python request dictionary and simply wrapped it in a multiline string (the triple quote `"""`) and the `retrieve` function
will do the rest.
Python dictionaries can be valid JSON strings, and the CDS requests builder is kind enough to make it so, (make sure the request string does not contain single quotes, but only double quotes)

Therefore simply calling `JSON.parse(request)` will return a valid Julia dictionary. This is done for you automatically as the call above will be translated into `CDSAPI.retrieve(dataset, JSON.parse(request), "download.nc")`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we should always recommend the method with string for end-users, and leave this technical detail about dicts for developers as comments in the source code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes agree, I'll change accordingly.


The previous method of calling `py2ju` is deprecated, but still available.

The `retrieve` function also accepts a `Dict` directly in case one needs to manipulate a base request:
```julia
using CDSAPI

dataset = "reanalysis-era5-single-levels"
request = """{
"product_type": ["reanalysis"],
"variable": [
"10m_u_component_of_wind",
"10m_v_component_of_wind"
],
"year": ["2024"],
"month": ["12"],
"day": ["06"],
"time": ["16:00"],
"data_format": "netcdf",
"download_format": "unarchived",
"area": [58, 6, 55, 9]
}"""

request_dict = JSON.parse(request)

We've copied/pasted the code and called the `py2ju` function on the second argument of the `retrieve` function.
The `py2ju` function simply converts the string containing a Python dictionary to an actual Julia dictionary.
#= ... Some operations with request_dict ... =#

CDSAPI.retrieve(dataset, request_dict, "download.nc")

```

Besides the downloaded file, the `retrieve` function also returns a dictionary with metadata:

Expand Down
24 changes: 18 additions & 6 deletions src/CDSAPI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ directory as `filename`.
The client periodically requests the status of the retrieve request.
`wait` is the maximum time (in seconds) between status updates.
"""
function retrieve(name, params, filename; wait=1.0)
function retrieve(name, params, filename; wait=1.0) end

function retrieve(name, params::AbstractDict, filename; wait=1.0)
creds = Dict()
open(joinpath(homedir(), ".cdsapirc")) do f
for line in readlines(f)
Expand All @@ -27,12 +29,12 @@ function retrieve(name, params, filename; wait=1.0)
["PRIVATE-TOKEN" => creds["key"]],
body=JSON.json(Dict("inputs" => params))
)
body = JSON.parse(String(response.body))
data = Dict("status" => "queued")

data = JSON.parse(String(response.body))
endpoint = Dict(response.headers)["location"]

while data["status"] != "successful"
data = HTTP.request("GET",
creds["url"] * "/retrieve/v1/jobs/" * string(body["jobID"]),
data = HTTP.request("GET", endpoint,
["PRIVATE-TOKEN" => creds["key"]]
)
data = JSON.parse(String(data.body))
Expand All @@ -53,7 +55,7 @@ function retrieve(name, params, filename; wait=1.0)
end

response = HTTP.request("GET",
creds["url"] * "/retrieve/v1/jobs/" * string(body["jobID"]) * "/results",
endpoint * "/results",
["PRIVATE-TOKEN" => creds["key"]]
)
body = JSON.parse(String(response.body))
Expand All @@ -62,6 +64,9 @@ function retrieve(name, params, filename; wait=1.0)
return data
end

retrieve(name, params::AbstractString, filename; wait=1.0) =
retrieve(name, JSON.parse(params), filename; wait)

"""
py2ju(dictstr)

Expand All @@ -88,6 +93,13 @@ Dict{String,Any} with 5 entries:
```
"""
function py2ju(dictstr)
Base.depwarn("""
The function `py2ju` is deprecated in favor of `JSON.parse` directly.
To update, simply replace the `py2ju` with `JSON.parse`. Making sure
that the request string does not contain single quotes (`'`) but only double quotes
(`"`).
Another option is to pass the request string directly. See the README.md for more examples.
""", :py2ju)
dictstr_cpy = replace(dictstr, "'" => "\"")
lastcomma_pos = findlast(",", dictstr_cpy).start

Expand Down