Files
services/quran/handler/quran.go
Asim Aslam f0542b1a6a Quran API (#205)
* Add basic version of a Quran API

* remove examples json swp

* fix search translation

* add examples json
2021-09-14 18:45:22 +01:00

198 lines
5.0 KiB
Go

package handler
import (
"context"
"fmt"
"net/url"
"github.com/micro/micro/v3/service/errors"
"github.com/micro/micro/v3/service/logger"
"github.com/micro/services/pkg/api"
"github.com/micro/services/quran/domain"
pb "github.com/micro/services/quran/proto"
)
const (
// the default api url
apiUrl = "https://api.quran.com/api/v4/"
// TODO: allow multiple translations
// the default translation id
translationId = "131,20"
// TODO: allow multiple interpretations
// the default tafsir id
tafsirId = "169"
// TODO: make configurable
arabicText = "text_imlaei"
)
type Quran struct{}
// Chapters returns a list of the chapters of the Quran
func (q *Quran) Chapters(ctx context.Context, req *pb.ChaptersRequest, rsp *pb.ChaptersResponse) error {
lang := "en"
if len(req.Language) > 0 {
lang = req.Language
}
var resp map[string][]*domain.Chapter
if err := api.Get(apiUrl+"chapters?language="+lang, &resp); err != nil {
logger.Errorf("Failed to retrieve chapters: %v", err)
return errors.InternalServerError("quran.chapters", "Failed to retrieve chapters")
}
for _, c := range resp["chapters"] {
rsp.Chapters = append(rsp.Chapters, &pb.Chapter{
Id: c.Id,
RevelationPlace: c.RevelationPlace,
RevelationOrder: c.RevelationOrder,
PrefixBismillah: c.BismillahPrefix,
Name: c.NameSimple,
ComplexName: c.NameComplex,
ArabicName: c.NameArabic,
TranslatedName: c.TranslatedName.Name,
Verses: c.VersesCount,
Pages: c.Pages,
})
}
return nil
}
// Retrieve the summary for a given chapter
func (q *Quran) Summary(ctx context.Context, req *pb.SummaryRequest, rsp *pb.SummaryResponse) error {
lang := "en"
if len(req.Language) > 0 {
lang = req.Language
}
if req.Chapter <= 0 {
return errors.BadRequest("quran.chapter-summary", "require chapter id")
}
var resp map[string]*domain.ChapterInfo
uri := fmt.Sprintf(apiUrl+"chapters/%d/info?language=%s", req.Chapter, lang)
if err := api.Get(uri, &resp); err != nil {
logger.Errorf("Failed to retrieve chapter info: %v", err)
return errors.InternalServerError("quran.chapter-summary", "Failed to retrieve chapter summary")
}
info := resp["chapter_info"]
rsp.Chapter = info.ChapterId
rsp.Summary = info.ShortText
rsp.Source = info.Source
rsp.Text = info.Text
return nil
}
// Return the verses by chapter
func (q *Quran) Verses(ctx context.Context, req *pb.VersesRequest, rsp *pb.VersesResponse) error {
lang := "en"
if len(req.Language) > 0 {
lang = req.Language
}
if req.Chapter <= 0 {
return errors.BadRequest("quran.verses", "require chapter id")
}
// TODO: enable configuring translations
// comma separated list of resource ids
// https://quran.api-docs.io/v4/resources/translations
translations := translationId
// TODO: enable configuring tafirs
// https://api.quran.com/api/v4/resources/tafsirs
tafsirs := tafsirId
uri := fmt.Sprintf(apiUrl+"verses/by_chapter/%d?language=%s", req.Chapter, lang)
// additional fields we require
// arabic text in imlaei script
uri += "&fields=" + arabicText
uri += "&words=true"
uri += "&word_fields=code_v2,text_imlaei"
if len(translations) > 0 && req.Translate {
uri += "&translations=" + translations
uri += "&translation_fields=resource_name"
}
if len(tafsirs) > 0 && req.Interpret {
uri += "&tafsirs=" + tafsirs
}
if req.Page > 0 {
uri += fmt.Sprintf("&page=%d", req.Page)
}
if req.Limit > 0 {
uri += fmt.Sprintf("&per_page=%d", req.Limit)
}
var resp *domain.VersesByChapter
if err := api.Get(uri, &resp); err != nil {
logger.Errorf("Failed to retrieve verses: %v", err)
return errors.InternalServerError("quran.verses", "Failed to retrieve verses")
}
rsp.Chapter = req.Chapter
rsp.Page = resp.Pagination.CurrentPage
rsp.TotalPages = resp.Pagination.TotalPages
for _, verse := range resp.Verses {
v := domain.VerseToProto(verse)
// strip words if not asked for
if req.Words != true {
v.Words = nil
}
rsp.Verses = append(rsp.Verses, v)
}
return nil
}
// Return the search results for a given query
func (q *Quran) Search(ctx context.Context, req *pb.SearchRequest, rsp *pb.SearchResponse) error {
if len(req.Query) == 0 {
return errors.BadRequest("quran.search", "missing search query")
}
lang := "en"
if len(req.Language) > 0 {
lang = req.Language
}
if req.Limit <= 0 {
req.Limit = 20
}
qq := url.Values{}
qq.Set("q", req.Query)
qq.Set("size", fmt.Sprintf("%d", req.Limit))
qq.Set("page", fmt.Sprintf("%d", req.Page))
qq.Set("language", lang)
uri := fmt.Sprintf(apiUrl+"search?%s", qq.Encode())
var resp map[string]*domain.SearchResults
if err := api.Get(uri, &resp); err != nil {
logger.Errorf("Failed to retrieve search results: %v", err)
return errors.InternalServerError("quran.search", "Failed to retrieve search results")
}
rsp.Query = req.Query
rsp.Page = resp["search"].CurrentPage
rsp.TotalPages = resp["search"].TotalPages
rsp.TotalResults = resp["search"].TotalResults
for _, result := range resp["search"].Results {
r := domain.ResultToProto(result)
rsp.Results = append(rsp.Results, r)
}
return nil
}