diff --git a/oblig1/endpoints/bookcount.go b/oblig1/endpoints/bookcount.go new file mode 100644 index 0000000000000000000000000000000000000000..c263fbef780a30ee9c7bd4ab413838cfd884da96 --- /dev/null +++ b/oblig1/endpoints/bookcount.go @@ -0,0 +1,153 @@ +package endpoints + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" +) + +// BookCountResponse represents the structure of the response for book count endpoint. +type BookCountResponse struct { + Language string `json:"language"` + Books int `json:"books"` + Authors int `json:"authors"` + Fraction float64 `json:"fraction"` +} + +type Books struct { + Count int `json:"count"` + Next string `json:"next"` + Previous string `json:"previous"` + Results []Result `json:"results"` +} +type Result struct { + Authors []Author `json:"authors"` +} +type Author struct { + Name string `json:"name"` +} + +// BookCountHandler handles requests to the /librarystats/v1/bookcount/ endpoint. +func BookCountHandler(w http.ResponseWriter, r *http.Request) { + languages := r.URL.Query().Get("language") + + // Split the language codes into a slice. + languageCodes := strings.Split(languages, ",") + + // Placeholder values for books, authors, and total books. + var totalBooks int + var uniqueAuthors = make(map[string]struct{}) // Use a map as a set for unique authors + var allBooks int + + // Create a slice to store results for each language. + var response []BookCountResponse + + // Iterate through each language code. + for _, language := range languageCodes { + // Placeholder for the URL of the first page of results. + gutendexAPIURL := fmt.Sprintf("http://129.241.150.113:8000/books/?languages=%v", language) + + // Fetch all pages of results. + for gutendexAPIURL != "" { + req, err := http.NewRequest("GET", gutendexAPIURL, nil) + if err != nil { + http.Error(w, "Failed to create a new request", http.StatusInternalServerError) + return + } + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + + res, err := client.Do(req) + if err != nil { + http.Error(w, "Failed to send a request to Gutendex API", http.StatusInternalServerError) + return + } + defer res.Body.Close() + + // Decode the response. + body, err := io.ReadAll(res.Body) + if err != nil { + http.Error(w, "Failed to read Gutendex API response", http.StatusInternalServerError) + return + } + + var books Books + err = json.Unmarshal(body, &books) + if err != nil { + http.Error(w, "Failed to decode Gutendex API response", http.StatusInternalServerError) + return + } + + // Increment total books and unique authors for each language. + totalBooks = books.Count + for _, book := range books.Results { + for _, author := range book.Authors { + uniqueAuthors[author.Name] = struct{}{} + } + } + + // Update total books for the first page only. + if allBooks == 0 { + allBooks = getBooksCount(w) + } + + // Update the URL to the next page of results. + gutendexAPIURL = books.Next + } + // Calculate fraction. + var fraction float64 + if allBooks > 0 { + fraction = float64(totalBooks) / float64(allBooks) + } + // Append results for the current language to the response slice. + response = append(response, BookCountResponse{ + Language: language, + Books: totalBooks, + Authors: len(uniqueAuthors), + Fraction: fraction, + }) + // Clear the set of unique authors for the next language. + uniqueAuthors = make(map[string]struct{}) + } + + // Send the response. + sendJSONResponse(w, response) +} + +func getBooksCount(w http.ResponseWriter) int { + gutendexCount := "http://129.241.150.113:8000/books/" + + req, err := http.NewRequest("GET", gutendexCount, nil) + if err != nil { + http.Error(w, "Failed to create a new request", http.StatusInternalServerError) + return 1 + } + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + + res, err := client.Do(req) + if err != nil { + http.Error(w, "Failed to send a request to Gutendex API", http.StatusInternalServerError) + return 1 + } + defer res.Body.Close() + + // Decode the response. + body, err := io.ReadAll(res.Body) + if err != nil { + http.Error(w, "Failed to read Gutendex API response", http.StatusInternalServerError) + return 1 + } + + var books Books + err = json.Unmarshal(body, &books) + if err != nil { + http.Error(w, "Failed to decode Gutendex API response", http.StatusInternalServerError) + return 1 + } + return books.Count +}