160 lines
3.8 KiB
Go
160 lines
3.8 KiB
Go
package flow
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/neatflowcv/seven-skies/internal/pkg/domain"
|
|
"github.com/neatflowcv/seven-skies/internal/pkg/repository"
|
|
"github.com/oklog/ulid/v2"
|
|
)
|
|
|
|
type Flow struct {
|
|
repo repository.Repository
|
|
}
|
|
|
|
func NewFlow(repo repository.Repository) *Flow {
|
|
return &Flow{
|
|
repo: repo,
|
|
}
|
|
}
|
|
|
|
func (f *Flow) CreateWeather(ctx context.Context, weather *Weather) error {
|
|
id := ulid.Make().String()
|
|
|
|
domainWeather := domain.NewWeather(
|
|
id,
|
|
domain.WeatherSource(weather.Source),
|
|
weather.TargetDate,
|
|
weather.ForecastDate,
|
|
domain.WeatherCondition(weather.Condition),
|
|
domain.Temperature{
|
|
Value: domain.Celsius(weather.Temperature),
|
|
},
|
|
)
|
|
|
|
err := f.repo.CreateWeather(ctx, domainWeather)
|
|
if err != nil {
|
|
if errors.Is(err, repository.ErrWeatherAlreadyExists) {
|
|
return ErrWeatherAlreadyExists
|
|
}
|
|
|
|
return fmt.Errorf("error in create weather: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *Flow) ListDailyWeathers(
|
|
ctx context.Context,
|
|
timezone string,
|
|
now time.Time,
|
|
) ([]*DailyWeather, error) {
|
|
location, err := time.LoadLocation(timezone)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error in load location: %w", err)
|
|
}
|
|
|
|
const aWeek = 7
|
|
|
|
localNow := now.In(location)
|
|
|
|
from := time.Date(localNow.Year(), localNow.Month(), localNow.Day(), 0, 0, 0, 0, location)
|
|
to := from.AddDate(0, 0, aWeek).Add(-time.Nanosecond)
|
|
|
|
weathers, err := f.repo.ListWeathers(ctx, from, to)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error in list daily weathers: %w", err)
|
|
}
|
|
|
|
weathersByDate := f.groupByDate(weathers, location)
|
|
|
|
var dailyWeathers []*DailyWeather
|
|
|
|
for dateKey, weathers := range weathersByDate {
|
|
newVar := f.mergeWeathers(dateKey, location, weathers)
|
|
dailyWeathers = append(dailyWeathers, newVar)
|
|
}
|
|
|
|
sort.Slice(dailyWeathers, func(i, j int) bool {
|
|
return dailyWeathers[i].Date.Before(dailyWeathers[j].Date)
|
|
})
|
|
|
|
return dailyWeathers, nil
|
|
}
|
|
|
|
func (*Flow) mergeWeathers(dateKey string, location *time.Location, weathers []*domain.Weather) *DailyWeather {
|
|
date, _ := time.Parse("2006-01-02", dateKey)
|
|
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, location)
|
|
|
|
// Source와 TargetDate가 동일한 경우, ForecastDate가 최신 항목만 선택
|
|
filteredWeathers := make(map[string]*domain.Weather)
|
|
|
|
for _, weather := range weathers {
|
|
key := string(weather.Source()) + weather.TargetDate().Format(time.DateTime)
|
|
|
|
existing, ok := filteredWeathers[key]
|
|
if !ok {
|
|
filteredWeathers[key] = weather
|
|
|
|
continue
|
|
}
|
|
|
|
if weather.ForecastDate().Before(existing.ForecastDate()) {
|
|
continue
|
|
}
|
|
|
|
filteredWeathers[key] = weather
|
|
}
|
|
|
|
var filteredSlice []*domain.Weather
|
|
for _, weather := range filteredWeathers {
|
|
filteredSlice = append(filteredSlice, weather)
|
|
}
|
|
|
|
if len(filteredSlice) == 0 {
|
|
panic("no weather found")
|
|
}
|
|
|
|
high := float64(filteredSlice[0].Temperature().Value)
|
|
low := float64(filteredSlice[0].Temperature().Value)
|
|
worstCondition := filteredSlice[0].Condition()
|
|
|
|
for _, weather := range filteredSlice {
|
|
temp := float64(weather.Temperature().Value)
|
|
if temp > high {
|
|
high = temp
|
|
}
|
|
|
|
if temp < low {
|
|
low = temp
|
|
}
|
|
|
|
if weather.Condition().IsWorseThan(worstCondition) {
|
|
worstCondition = weather.Condition()
|
|
}
|
|
}
|
|
|
|
return &DailyWeather{
|
|
Date: date,
|
|
High: high,
|
|
Low: low,
|
|
Condition: string(worstCondition),
|
|
}
|
|
}
|
|
|
|
func (*Flow) groupByDate(weathers []*domain.Weather, location *time.Location) map[string][]*domain.Weather {
|
|
weathersByDate := make(map[string][]*domain.Weather)
|
|
|
|
for _, weather := range weathers {
|
|
localDate := weather.TargetDate().In(location)
|
|
dateKey := time.Date(localDate.Year(), localDate.Month(), localDate.Day(), 0, 0, 0, 0, location).Format(time.DateOnly)
|
|
weathersByDate[dateKey] = append(weathersByDate[dateKey], weather)
|
|
}
|
|
|
|
return weathersByDate
|
|
}
|