feat: Initial prototype

This commit is contained in:
2023-03-04 18:56:03 +01:00
commit 415a6c8337
9 changed files with 578 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
package diyanet
import (
"context"
"fmt"
"regexp"
"time"
"github.com/PuerkitoBio/goquery"
"prayertimes/pkg/prayer"
)
type Fetcher interface {
FetchParsed(ctx context.Context, url string) (*goquery.Document, error)
}
type Diyanet struct {
FetcherFunc func(ctx context.Context, url string) (*goquery.Document, error)
}
var reNumeric = regexp.MustCompile(`\d+`)
func validateLocation(location string) error {
if !reNumeric.MatchString(location) {
return fmt.Errorf("invalid location id")
}
return nil
}
func (d Diyanet) Get(ctx context.Context, location string) ([]prayer.Times, error) {
if err := validateLocation(location); err != nil {
return nil, fmt.Errorf("%w: %v", prayer.ErrInvalidLocation, err)
}
u := fmt.Sprintf("https://namazvakitleri.diyanet.gov.tr/en-US/%s", location)
doc, err := d.FetcherFunc(ctx, u)
if err != nil {
return nil, fmt.Errorf("failed to fetch location %q: %w", location, err)
}
var times []prayer.Times
doc.Find("#tab-1 .vakit-table tbody tr").Each(func(_ int, el *goquery.Selection) {
date := el.Find("td:first-of-type").Text()
parsedDate, err := time.Parse("02.01.2006", date)
if err != nil {
return
}
row := prayer.Times{
Date: parsedDate,
Fajr: el.Find("td:nth-of-type(2)").Text(),
Sunrise: el.Find("td:nth-of-type(3)").Text(),
Dhuhr: el.Find("td:nth-of-type(4)").Text(),
Asr: el.Find("td:nth-of-type(5)").Text(),
Maghrib: el.Find("td:nth-of-type(6)").Text(),
Isha: el.Find("td:nth-of-type(7)").Text(),
}
times = append(times, row)
})
return times, err
}
+113
View File
@@ -0,0 +1,113 @@
package diyanet
import (
"context"
"strings"
"testing"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"prayertimes/internal/scrapeutils"
"prayertimes/pkg/prayer"
)
type mockFetcher string
func (f mockFetcher) Fetch(_ context.Context, _ string) (*goquery.Document, error) {
return goquery.NewDocumentFromReader(strings.NewReader(string(f)))
}
const mockHtml = `
<div id='tab-1'>
<div class='table-responsive'>
<table class='table vakit-table'>
<caption>Monthly Prayer Times for Some Location</caption>
<thead>
<tr>
<th>Gregorian Calendar Date</th>
<th>Fajr</th>
<th>Sun</th>
<th>Dhuhr</th>
<th>Asr</th>
<th>Maghrib</th>
<th>Isha</th>
</tr>
</thead>
<tbody>
<tr>
<td>04.03.2023</td>
<td>05:48</td>
<td>07:11</td>
<td>13:05</td>
<td>16:15</td>
<td>18:49</td>
<td>20:06</td>
</tr>
<tr>
<td>05.03.2023</td>
<td>05:46</td>
<td>07:09</td>
<td>13:04</td>
<td>16:15</td>
<td>18:50</td>
<td>20:07</td>
</tr>
</tbody>
</table>
</div>
</div>
`
func TestDiyanet_Get(t *testing.T) {
t.Run("validates location", func(t *testing.T) {
d := Diyanet{}
_, err := d.Get(context.Background(), " not numeric ")
assert.ErrorIs(t, err, prayer.ErrInvalidLocation)
})
t.Run("extracts prayer times", func(t *testing.T) {
d := Diyanet{
FetcherFunc: mockFetcher(mockHtml).Fetch,
}
actual, err := d.Get(context.Background(), "1234")
assert.NoError(t, err)
expected := []prayer.Times{
{
Date: time.Date(2023, time.March, 4, 0, 0, 0, 0, time.UTC),
Fajr: "05:48",
Sunrise: "07:11",
Dhuhr: "13:05",
Asr: "16:15",
Maghrib: "18:49",
Isha: "20:06",
},
{
Date: time.Date(2023, time.March, 5, 0, 0, 0, 0, time.UTC),
Fajr: "05:46",
Sunrise: "07:09",
Dhuhr: "13:04",
Asr: "16:15",
Maghrib: "18:50",
Isha: "20:07",
},
}
assert.Equal(t, expected, actual)
})
t.Run("real endpoint", func(t *testing.T) {
if testing.Short() {
t.Skip()
}
d := Diyanet{
FetcherFunc: scrapeutils.GetParsed,
}
times, err := d.Get(context.Background(), "9205")
assert.NoError(t, err)
assert.Greater(t, len(times), 1)
assert.NotZero(t, times[0].Date)
})
}