From 8f0c0870144190286af201b555505b11915a1e4a Mon Sep 17 00:00:00 2001 From: Abdulsamad Sheikh <abdulsas@stud.ntnu.no> Date: Mon, 26 Feb 2024 10:20:31 +0100 Subject: [PATCH] Updated all files --- handlers/handlers.go | 75 +++++++++++++++++++++++++++++++++----------- models/models.go | 16 ++++++++++ services/services.go | 68 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 18 deletions(-) diff --git a/handlers/handlers.go b/handlers/handlers.go index 66b09cd..cd1b2e9 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -1,40 +1,79 @@ package handlers import ( - // ... other imports + "encoding/json" + "net/http" "assignment1/models" "assignment1/services" + "fmt" ) -func BookCountHandler(w http.ResponseWriter, r *http.Request) { - // Parse query parameters for language +func ReadershipHandler(w http.ResponseWriter, r *http.Request) { + // Extract the language code from the URL path + language := r.URL.Path[len("/librarystats/v1/readership/"):] + + // Optional: Parse query parameters for limit if present query := r.URL.Query() - language := query.Get("language") + limitParam := query.Get("limit") + var limit int + if limitParam != "" { + fmt.Sscanf(limitParam, "%d", &limit) + } // Fetch book data from the Gutendex API books, err := services.FetchBooksByLanguage(language) if err != nil { - // Handle error http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Process the books to calculate the book count and author count - // ... (Implementation required) + // Fetch countries for the given language + countries, err := services.FetchCountriesByLanguage(language) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Fetch population data for each country and calculate total readership + var readershipDetails []models.ReadershipDetail + var totalReadership int64 = 0 + for _, country := range countries.Countries { + population, err := services.FetchPopulationByCountryCode(country.ISOCode) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + totalReadership += population - // Create a response object - response := models.GutenbergBookCount{ - Language: language, - // Books: Number of books (to be calculated), - // Authors: Number of unique authors (to be calculated), - // Fraction: Fraction of books in the language (to be calculated), + readershipDetail := models.ReadershipDetail{ + Country: country.Name, + ISOCode: country.ISOCode, + Books: len(books.Results), + Authors: services.CalculateUniqueAuthors(books.Results), + Readership: population, + } + readershipDetails = append(readershipDetails, readershipDetail) + + // Apply limit if specified + if limit > 0 && len(readershipDetails) >= limit { + break + } } - // Write the response back as JSON + // Create and send the response w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(response); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + json.NewEncoder(w).Encode(readershipDetails) } -// ... Other handlers +func StatusHandler(w http.ResponseWriter, r *http.Request) { + status := models.ServiceStatus{ + GutendexAPI: services.CheckServiceAvailability("http://129.241.150.113:8000/books/"), + LanguageAPI: services.CheckServiceAvailability("http://129.241.150.113:3000/language2countries/"), + CountriesAPI: services.CheckServiceAvailability("http://129.241.150.113:8080/v3.1/"), + Version: "v1", + Uptime: services.GetUptime(), // Implement GetUptime in your services package + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(status) +} diff --git a/models/models.go b/models/models.go index ea40e15..05b86cd 100644 --- a/models/models.go +++ b/models/models.go @@ -31,3 +31,19 @@ type Person struct { DeathYear *int `json:"death_year"` Name string `json:"name"` } + +// LanguageCountriesResponse represents the response structure from the Language2Countries API. +type LanguageCountriesResponse struct { + Countries []CountryInfo `json:"countries"` +} + +// CountryInfo represents country information in the LanguageCountriesResponse. +type CountryInfo struct { + Name string `json:"name"` + ISOCode string `json:"isoCode"` +} + +// CountryResponse represents the structure of a country response from the REST Countries API. +type CountryResponse struct { + Population int64 `json:"population"` +} diff --git a/services/services.go b/services/services.go index f9e1eda..9962bf1 100644 --- a/services/services.go +++ b/services/services.go @@ -23,3 +23,71 @@ func FetchBooksByLanguage(language string) (*models.GutenbergResponse, error) { return &booksResponse, nil } + + +// FetchCountriesByLanguage retrieves countries for a given language code. +func FetchCountriesByLanguage(language string) (*models.LanguageCountriesResponse, error) { + url := "http://129.241.150.113:3000/language2countries/" + language + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var countriesResponse models.LanguageCountriesResponse + if err := json.NewDecoder(resp.Body).Decode(&countriesResponse); err != nil { + return nil, err + } + + return &countriesResponse, nil +} + +// FetchPopulationByCountryCode retrieves population for a given country code. +func FetchPopulationByCountryCode(isoCode string) (int64, error) { + url := "http://129.241.150.113:8080/v3.1/alpha/" + isoCode + resp, err := http.Get(url) + if err != nil { + return 0, err + } + defer resp.Body.Close() + + var countryResponse []models.CountryResponse // Assuming CountryResponse is an array + if err := json.NewDecoder(resp.Body).Decode(&countryResponse); err != nil { + return 0, err + } + + if len(countryResponse) > 0 { + return countryResponse[0].Population, nil + } + + return 0, nil // Or return an error if no country is found +} + +// CalculateUniqueAuthors calculates the number of unique authors from a list of books. +func CalculateUniqueAuthors(books []models.GutenbergBook) int { + authorsSet := make(map[string]struct{}) + for _, book := range books { + for _, author := range book.Authors { + authorsSet[author.Name] = struct{}{} + } + } + return len(authorsSet) +} + +// CheckServiceAvailability checks the availability of a given service URL. +func CheckServiceAvailability(serviceURL string) string { + resp, err := http.Get(serviceURL) + if err != nil { + return "Unavailable" + } + defer resp.Body.Close() + return http.StatusText(resp.StatusCode) +} + +// Assuming you have a global variable to track the start time of your service. +var serviceStartTime = time.Now() + +// GetUptime calculates the uptime of the service in seconds. +func GetUptime() int64 { + return int64(time.Since(serviceStartTime).Seconds()) +} -- GitLab