Source file DBLP.ml

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
    (* Printf.printf "Response code: %d\n" code; *)
    (* Printf.printf "Headers: %s\n" (resp |> Response.headers |> Header.to_string); *)
    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";
      }
    )