book.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. package wikidata
  2. import (
  3. "notabug.org/apiote/amuse/datastructure"
  4. "notabug.org/apiote/amuse/network"
  5. "database/sql"
  6. "encoding/json"
  7. "errors"
  8. "net/http"
  9. "strconv"
  10. "strings"
  11. "notabug.org/apiote/gott"
  12. )
  13. type Editions struct {
  14. Entities map[string]struct {
  15. OriginalLang string
  16. Image struct {
  17. Url string
  18. }
  19. }
  20. }
  21. func queryBookByTmdb(args ...interface{}) (interface{}, error) {
  22. id := args[0].(*network.Request).Id
  23. tag := args[0].(*network.Request).Language[:2]
  24. result := args[1].(*Result)
  25. repo := result.Repo
  26. res, err := repo.Query(`SELECT ?book ?bookLabel ?bookseries ?bookseriesLabel WHERE {
  27. OPTIONAL {
  28. ?serie wdt:P4983 "` + id + `";
  29. wdt:P144 ?book.
  30. ?book wdt:P31 wd:Q47461344.
  31. }
  32. OPTIONAL {
  33. ?film wdt:P4947 "` + id + `";
  34. wdt:P144 ?book.
  35. ?book wdt:P31 wd:Q47461344.
  36. }
  37. OPTIONAL {
  38. ?film wdt:P4947 "` + id + `";
  39. wdt:P144 ?bookseries.
  40. ?bookseries (wdt:P31/(wdt:P279*)) wd:Q277759.
  41. }
  42. OPTIONAL {
  43. ?serie wdt:P4983 "` + id + `";
  44. wdt:P144 ?bookseries.
  45. ?bookseries (wdt:P31/(wdt:P279*)) wd:Q277759.
  46. }
  47. SERVICE wikibase:label { bd:serviceParam wikibase:language "` + tag + `", "en". }
  48. }`)
  49. result.Result = res
  50. return gott.Tuple(args), err
  51. }
  52. func queryBook(args ...interface{}) (interface{}, error) {
  53. id := args[0].(*network.Request).Id
  54. tag := args[0].(*network.Request).Language[:2]
  55. result := args[1].(*Result)
  56. repo := result.Repo
  57. res, err := repo.Query(`SELECT ?title ?bookLabel ?authorLabel ?series ?seriesLabel ?ordinal ?genreLabel (YEAR(?publication) AS ?year) ?article_title WHERE {
  58. ` + id + ` wdt:P50 ?author.
  59. ` + id + ` wdt:P1476 ?title.
  60. optional {
  61. ` + id + ` wdt:P577 ?publication.
  62. }
  63. optional {
  64. ` + id + ` wdt:P136 ?genre.
  65. }
  66. OPTIONAL {
  67. ?article schema:about ` + id + `;
  68. schema:inLanguage "` + tag + `";
  69. schema:name ?article_title;
  70. schema:isPartOf _:b18.
  71. _:b18 wikibase:wikiGroup "wikipedia".
  72. FILTER(!(CONTAINS(?article_title, ":")))
  73. }
  74. OPTIONAL {
  75. ` + id + ` wdt:P179 ?series.
  76. OPTIONAL { ` + id + ` wdt:P1545 ?ordinal. }
  77. }
  78. SERVICE wikibase:label { bd:serviceParam wikibase:language "` + tag + `", "en". }
  79. SERVICE wikibase:label {
  80. bd:serviceParam wikibase:language "` + tag + `", "en".
  81. ` + id + ` rdfs:label ?bookLabel.
  82. }
  83. }`)
  84. result.Result = res
  85. return gott.Tuple(args), err
  86. }
  87. func parseBook(args ...interface{}) (interface{}, error) {
  88. id := args[0].(*network.Request).Id
  89. results := args[1].(*Result)
  90. res := results.Result
  91. if res.Results.Bindings == nil || len(res.Results.Bindings) == 0 {
  92. results.Work = &datastructure.Book{}
  93. } else {
  94. authors := map[string]bool{}
  95. genres := map[string]bool{}
  96. result := res.Results.Bindings[0]
  97. var year int64
  98. var err error
  99. if result["year"].Value != "" {
  100. year, err = strconv.ParseInt(result["year"].Value, 10, 64)
  101. if err != nil {
  102. return gott.Tuple(args), err
  103. }
  104. }
  105. bookId := id
  106. if result["book"].Value != "" {
  107. bookId = strings.Replace(result["book"].Value, "http://www.wikidata.org/entity/", "wd:", 1)
  108. }
  109. title := result["bookLabel"].Value
  110. if title == strings.Replace(bookId, "wd:", "", 1) || title == "" {
  111. title = result["title"].Value
  112. }
  113. book := datastructure.Book{
  114. Id: bookId,
  115. Uri: "/books/" + bookId,
  116. Source: []datastructure.Source{},
  117. Title: title,
  118. Year: year,
  119. SerieUri: strings.Replace(result["series"].Value, "http://www.wikidata.org/entity/", "/bookseries/wd:", 1),
  120. SerieName: result["seriesLabel"].Value,
  121. PartInSerie: result["ordinal"].Value,
  122. Authors: []string{},
  123. Genres: []string{},
  124. Article: result["article_title"].Value,
  125. }
  126. book.Source = append(book.Source, datastructure.Source{
  127. Url: "https://www.wikidata.org/wiki/" + strings.Replace(id, "wd:", "", 1),
  128. Name: "Wikidata",
  129. })
  130. book.Source = append(book.Source, datastructure.Source{
  131. Url: "https://inventaire.io/entity/" + id,
  132. Name: "Inventaire",
  133. })
  134. if result["bookseries"].Value != "" {
  135. book.Uri = strings.Replace(result["bookseries"].Value, "http://www.wikidata.org/entity/", "/bookseries/wd:", 1)
  136. book.Title = result["bookseriesLabel"].Value
  137. }
  138. for _, r := range res.Results.Bindings {
  139. authors[r["authorLabel"].Value] = true
  140. if r["genreLabel"].Value != "" {
  141. genres[r["genreLabel"].Value] = true
  142. }
  143. }
  144. for k := range authors {
  145. book.Authors = append(book.Authors, k)
  146. }
  147. for k := range genres {
  148. book.Genres = append(book.Genres, k)
  149. }
  150. results.Work = &book
  151. }
  152. return gott.Tuple(args), nil
  153. }
  154. func createIsbnRequest(args ...interface{}) (interface{}, error) {
  155. descriptionRequest := args[0].(*network.Request)
  156. result := args[1].(*network.Result)
  157. result.Client = &http.Client{}
  158. id := descriptionRequest.Id
  159. request, err := http.NewRequest("GET", "https://inventaire.io/api/entities?action=reverse-claims&property=wdt:P629&value="+id, nil)
  160. result.Request = request
  161. return gott.Tuple(args), err
  162. }
  163. func unmarshalIsbn(args ...interface{}) (interface{}, error) {
  164. var isbns struct{ Uris []string }
  165. result := args[1].(*network.Result)
  166. err := json.Unmarshal(result.Body, &isbns)
  167. uris := ""
  168. for _, isbn := range isbns.Uris {
  169. uris += "|" + isbn
  170. }
  171. if len(uris) > 0 {
  172. uris = uris[1:]
  173. }
  174. result.Result = uris
  175. return gott.Tuple(args), err
  176. }
  177. func getIsbn(args ...interface{}) (interface{}, error) {
  178. request := args[0].(*network.Request)
  179. res, err := gott.
  180. NewResult(gott.Tuple{request, &network.Result{}}).
  181. Bind(createIsbnRequest).
  182. Bind(network.DoRequest).
  183. Bind(network.HandleRequestError).
  184. Bind(network.ReadResponse).
  185. Bind(unmarshalIsbn).
  186. Finish()
  187. request.Id = res.(gott.Tuple)[1].(*network.Result).Result.(string)
  188. return gott.Tuple(args), err
  189. }
  190. func checkEmpty(args ...interface{}) (interface{}, error) {
  191. request := args[0].(*network.Request)
  192. if request.Id == "" {
  193. return gott.Tuple(args), errors.New("warning: empty ISBN")
  194. }
  195. return gott.Tuple(args), nil
  196. }
  197. func createEditionsRequest(args ...interface{}) (interface{}, error) {
  198. editionsRequest := args[0].(*network.Request)
  199. result := args[1].(*network.Result)
  200. result.Client = &http.Client{}
  201. id := editionsRequest.Id
  202. request, err := http.NewRequest("GET", "https://inventaire.io/api/entities?action=by-uris&uris="+id+"&refresh=false", nil)
  203. result.Request = request
  204. return gott.Tuple(args), err
  205. }
  206. func unmarshalEditions(args ...interface{}) (interface{}, error) {
  207. var editions Editions
  208. result := args[1].(*network.Result)
  209. err := json.Unmarshal(result.Body, &editions)
  210. result.Result = editions
  211. return gott.Tuple(args), err
  212. }
  213. func filterEditionLanguage(args ...interface{}) interface{} {
  214. result := args[1].(*network.Result)
  215. editions := result.Result.(Editions)
  216. language := args[0].(*network.Request).Language[:2]
  217. covers := []string{}
  218. englishCover := ""
  219. for _, edition := range editions.Entities {
  220. if edition.OriginalLang == language && edition.Image.Url != "" {
  221. covers = append(covers, "https://inventaire.io"+edition.Image.Url)
  222. }
  223. if edition.OriginalLang == "en" && englishCover == "" && edition.Image.Url != "" {
  224. englishCover = "https://inventaire.io" + edition.Image.Url
  225. }
  226. }
  227. if len(covers) == 0 {
  228. result.Result = englishCover
  229. } else {
  230. result.Result = covers[0]
  231. }
  232. return gott.Tuple(args)
  233. }
  234. func getCover(args ...interface{}) (interface{}, error) {
  235. request := args[0].(*network.Request)
  236. res, err := gott.
  237. NewResult(gott.Tuple{request, &network.Result{}}).
  238. Bind(createEditionsRequest).
  239. Bind(network.DoRequest).
  240. Bind(network.HandleRequestError).
  241. Bind(network.ReadResponse).
  242. Bind(unmarshalEditions).
  243. Map(filterEditionLanguage).
  244. Finish()
  245. args[1] = res.(gott.Tuple)[1].(*network.Result).Result.(string)
  246. return gott.Tuple(args), err
  247. }
  248. func GetBookByTmdb(id, language string) (datastructure.Book, error) {
  249. res, err := gott.
  250. NewResult(gott.Tuple{&network.Request{Id: id, Language: language}, &Result{}}).
  251. Bind(createRepo).
  252. Bind(queryBookByTmdb).
  253. Bind(parseBook).
  254. Finish()
  255. if err != nil {
  256. return datastructure.Book{}, err
  257. }
  258. book := res.(gott.Tuple)[1].(*Result).Work.(*datastructure.Book)
  259. return *book, nil
  260. }
  261. func GetBook(id, language string, connection *sql.DB) (*datastructure.Book, error) {
  262. res, err := gott.
  263. NewResult(gott.Tuple{&network.Request{Id: id, Language: language}, &Result{}}).
  264. Bind(createRepo).
  265. Bind(queryBook).
  266. Bind(parseBook).
  267. Finish()
  268. if err != nil {
  269. return &datastructure.Book{}, err
  270. }
  271. return res.(gott.Tuple)[1].(*Result).Work.(*datastructure.Book), nil
  272. }
  273. func GetCover(id, language string, connection *sql.DB) (string, error) {
  274. res, err := gott.
  275. NewResult(gott.Tuple{&network.Request{Id: id, Language: language, Connection: connection}, ""}).
  276. Bind(getIsbn).
  277. Bind(checkEmpty).
  278. Bind(getCover).
  279. Finish()
  280. if err != nil && strings.HasPrefix(err.Error(), "warning") {
  281. return "", nil
  282. } else if err != nil {
  283. return "", err
  284. } else {
  285. return res.(gott.Tuple)[1].(string), nil
  286. }
  287. }