Skip to content

Commit e9cd499

Browse files
examples: make get_weather use x.json2 and add translation to a user selectable language, defaulting to English (#25368)
1 parent fcf9228 commit e9cd499

2 files changed

Lines changed: 69 additions & 47 deletions

File tree

‎examples/get_weather/README.md‎

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
# get_weather
2-
get_weather is a very simple web crawler.
3-
Its goal is to get a weather forecast from caiyunapp.com.
42

5-
# Compile and Run
3+
get_weather is a web crawler. Its goal is to get a weather forecast from
4+
`https://api.caiyunapp.com` in chinese and translated to another selected language.
65

7-
Use this to generate an executable and then launch the web crawler.
8-
```bash
9-
v get_weather.v
10-
./get_weather
11-
```
6+
We use `http.fetch()` to get a forecast `http.Response`, with a custom user-agent
7+
and then we decode the json into a struct with only relevant fields `lang`,
8+
`result` and `forecast_keypoint`.
9+
10+
The chinese texts are translated with another `http.fetch()` to
11+
`https://translate.googleapis.com` and then decoding the `http.Response` as `json2.Any` arrays.
12+
13+
## running
14+
15+
By default texts are translated to English. Another language can be indicated
16+
with first argument as an [ISO 639](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes) code:
1217

13-
As a convenience, you can also compile and launch the web crawler directly.
1418
```bash
15-
v run get_weather.v
19+
$ v run examples/get_weather/get_weather.v
20+
zh_CN: 未来两小时天气
21+
en: Weather in the next two hours
22+
zh_CN: 最近的降雨带在西北66公里外呢
23+
en: The nearest rainfall zone is 66 kilometers northwest
24+
$
25+
$ v run examples/get_weather/get_weather.v es
26+
zh_CN: 未来两小时天气
27+
es: Clima en las próximas dos horas
28+
zh_CN: 最近的降雨带在西北66公里外呢
29+
es: La zona de lluvia más cercana está a 66 kilómetros al noroeste
1630
```
1731

18-
In this project we use http.fetch() to get a http.Response, with a
19-
custom user-agent and then we use json.decode() to decode the json
20-
response to struct.
21-
We also use a `[skip]` attribute to skip certain fields in the response,
22-
that we don't need and use a `[json: result]` attribute to specify that
23-
our struct field is named differently from the incoming json response.

‎examples/get_weather/get_weather.v‎

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,69 @@
1-
import json
2-
import rand
31
import net.http
2+
import os
3+
import rand
4+
import x.json2
5+
import x.json2.decoder2 as json
46

57
struct Weather {
6-
status string @[skip] // drop this field
7-
api_version string @[skip]
8-
api_status string @[skip]
9-
lang string @[skip]
10-
unit string @[skip]
11-
tzshift int @[skip]
12-
timezone string @[skip]
13-
server_time u32 @[skip]
14-
location []f32 @[skip]
15-
result Result //@[json: result] if the field name is different in JSON, it can be specified
8+
lang string
9+
result Result
1610
}
1711

1812
struct Result {
19-
realtime Realtime @[skip]
20-
minutely Minutely @[skip]
21-
hourly Hourly @[skip]
22-
daily Daily @[skip]
23-
primary int @[skip]
2413
forecast_keypoint string
2514
}
2615

27-
struct Realtime {}
28-
29-
struct Minutely {}
30-
31-
struct Hourly {}
32-
33-
struct Daily {}
16+
const config = http.FetchConfig{
17+
user_agent: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0'
18+
}
3419

3520
fn main() {
36-
config := http.FetchConfig{
37-
user_agent: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0'
38-
}
39-
21+
dest_lang := if os.args.len > 1 { os.args[1] } else { 'en' }
4022
rnd := rand.f32()
4123
url := 'https://api.caiyunapp.com/v2.5/96Ly7wgKGq6FhllM/116.391912,40.010711/weather.jsonp?hourlysteps=120&random=${rnd}'
42-
// println(url)
4324

4425
resp := http.fetch(http.FetchConfig{ ...config, url: url }) or {
4526
println('failed to fetch data from the server')
4627
return
4728
}
4829

49-
weather := json.decode(Weather, resp.body) or {
30+
weather := json.decode[Weather](resp.body) or {
5031
println('failed to decode weather json')
5132
return
5233
}
5334

54-
println('未来两小时天气:\n${weather.result.forecast_keypoint}.')
35+
for ch in ['未来两小时天气', weather.result.forecast_keypoint] {
36+
println('${weather.lang:8}: ${ch}')
37+
t := translate(ch, weather.lang, dest_lang) or {
38+
println('failed to translate ${ch}: ${err}')
39+
continue
40+
}
41+
println('${dest_lang:8}: ${t}')
42+
}
43+
}
44+
45+
// translate fetch google to print translate text `q` in languate `sl` into language `tl`.
46+
// A translation typical response json is like: `[[["Weather in the next two hours",
47+
// "未来两小时天气",null,null,3,null,null,[[null,"offline"]],
48+
// [[["61549914d65604307a34fd1855292577","offline_launch_doc.md"],null,null,null,null,
49+
// [[[6,8,0]]]]]]],null,"zh-CN",null,null,null,null,[]]`
50+
// where translated text is located at position `json_resp[0][0][0]`.
51+
fn translate(q string, sl string, tl string) !string {
52+
url := 'https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sl}&tl=${tl}&dt=t&q=${q}'
53+
54+
resp := http.fetch(http.FetchConfig{ ...config, url: url })!
55+
56+
json_resp := json.decode[json2.Any](resp.body)!
57+
58+
a := json_resp.arr()
59+
if a.len > 0 {
60+
a0 := a[0].arr()
61+
if a0.len > 0 {
62+
a00 := a0[0].arr()
63+
if a00.len > 0 {
64+
return a00[0].str()
65+
}
66+
}
67+
}
68+
return error('invalid translation response')
5569
}

0 commit comments

Comments
 (0)