1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
let download url =
let download url =
let open Lwt in
let open Cohttp in
let open Cohttp_lwt_unix in
Client.get (Uri.of_string url) >>= fun (resp, body) ->
let code = resp |> Response.status |> Code.code_of_status in
if code <> 200 then failwith (Printf.sprintf "Server response code was %d instead of expected 200" code);
body |> Cohttp_lwt.Body.to_string >|= fun body ->
body
in
Lwt_main.run (download url)
module JSON = struct
let of_string = Yojson.Basic.from_string
let list = function
| `List l -> l
| _ -> failwith "list expected"
let assoc = function
| `Assoc a -> a
| _ -> failwith "assoc expected"
let associate k json =
json |> assoc |> List.assoc k
let associate_opt k json =
json |> assoc |> List.assoc_opt k
let string = function
| `String s -> s
| _ -> failwith "string expected"
end
let query ?hits ?first ?completion kind query =
let hits =
match hits with
| Some h -> "h=" ^ string_of_int h ^ "&"
| None -> ""
in
let first =
match first with
| Some f -> "f=" ^ string_of_int f ^ "&"
| None -> ""
in
let completion =
match completion with
| Some c -> "c=" ^ string_of_int c ^ "&"
| None -> ""
in
let kind =
match kind with
| `Publication -> "publ"
| `Author -> "author"
| `Venue -> "venue"
in
let url = "https://dblp.org/search/" ^ kind ^ "/api?format=json&" ^ hits ^ first ^ completion ^ "q=" ^ query in
download url
let query_json ?hits kind q =
query ?hits kind q |> JSON.of_string
let json_hits json =
let hits =
json
|> JSON.associate "result"
|> JSON.associate "hits"
|> JSON.associate_opt "hit"
in
match hits with
| None -> []
| Some hits ->
hits
|> JSON.list
|> List.map (fun h -> h |> JSON.associate "info" |> JSON.assoc)
type author =
{
author_name : string;
author_url : string;
}
let author ?hits name =
query_json ?hits `Author name |> json_hits |>
List.map (fun author ->
let find k = List.assoc k author |> JSON.string in
{
author_name = find "author";
author_url = find "url";
}
)
type publication =
{
publication_authors : string list;
publication_title : string;
publication_venue : string;
publication_pages : string;
publication_year : int;
publication_type : string;
publication_key : string;
publication_doi : string;
publication_url : string;
publication_ee : string;
publication_access : string;
publication_bib : unit -> string;
}
let publication ?hits query =
query_json ?hits `Publication query |> json_hits |>
List.map (fun publication ->
let find k = List.assoc_opt k publication |> Option.map JSON.string |> Option.value ~default:"" in
let publication_authors =
let author = List.assoc "authors" publication |> JSON.associate "author" in
match author with
| `List l -> List.map (fun a -> a |> JSON.associate "text" |> JSON.string) l
| `Assoc a -> [List.assoc "text" a |> JSON.string]
| _ -> assert false
in
let url = find "url" in
{
publication_authors;
publication_title = find "title";
publication_venue = find "venue";
publication_pages = find "pages";
publication_year = find "year" |> int_of_string;
publication_type = find "type";
publication_key = find "key";
publication_doi = find "doi";
publication_url = url;
publication_ee = find "ee";
publication_access = find "access";
publication_bib = fun () -> download (url ^ ".bib")
}
)
let string_of_publication p =
let authors = p.publication_authors |> String.concat ", " in
let pages = if p.publication_pages = "" then "" else p.publication_pages ^ ", " in
Printf.sprintf "%s. %s %s, %s%d." authors p.publication_title p.publication_venue pages p.publication_year
type venue =
{
venue_name : string;
venue_acronym : string;
venue_type : string;
venue_url : string;
}
let venue ?hits venue =
query_json ?hits `Venue venue |> json_hits |>
List.map (fun venue ->
let find k = List.assoc_opt k venue |> Option.map JSON.string |> Option.value ~default:"" in
{
venue_name = find "venue";
venue_acronym = find "acronym";
venue_type = find "type";
venue_url = find "url";
}
)