Refactored the application structure to better separate concerns between files and packages
Some checks failed
ci/woodpecker/push/build-image Pipeline failed
Some checks failed
ci/woodpecker/push/build-image Pipeline failed
This commit is contained in:
96
internal/adapters/outbound/postgres/forecast_read.go
Normal file
96
internal/adapters/outbound/postgres/forecast_read.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// forecast_read.go executes hourly forecast and period queries.
|
||||
// Layer: adapters/outbound/postgres forecast feature.
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
||||
)
|
||||
|
||||
func (r *Repository) LatestHourlyForecast(ctx context.Context) (*model.WeatherForecastRun, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, fmt.Errorf("postgres repository is not configured")
|
||||
}
|
||||
|
||||
var row forecastParentRow
|
||||
err := r.db.QueryRowContext(ctx, queryLatestHourlyForecast).Scan(
|
||||
&row.EventID,
|
||||
&row.LocationID,
|
||||
&row.LocationName,
|
||||
&row.IssuedAt,
|
||||
&row.UpdatedAt,
|
||||
&row.Product,
|
||||
&row.Latitude,
|
||||
&row.Longitude,
|
||||
&row.ElevationMeters,
|
||||
)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query latest hourly forecast: %w", err)
|
||||
}
|
||||
|
||||
run := mapForecastParentRow(row)
|
||||
|
||||
periods, err := r.loadForecastPeriods(ctx, row.EventID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
run.Periods = periods
|
||||
|
||||
return &run, nil
|
||||
}
|
||||
|
||||
func (r *Repository) loadForecastPeriods(ctx context.Context, eventID string) ([]model.WeatherForecastPeriod, error) {
|
||||
rows, err := r.db.QueryContext(ctx, queryForecastPeriods, eventID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query forecast periods: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make([]model.WeatherForecastPeriod, 0)
|
||||
for rows.Next() {
|
||||
var row forecastPeriodRow
|
||||
if err := rows.Scan(
|
||||
&row.PeriodIndex,
|
||||
&row.StartTime,
|
||||
&row.EndTime,
|
||||
&row.Name,
|
||||
&row.IsDay,
|
||||
&row.ConditionCode,
|
||||
&row.ConditionText,
|
||||
&row.ProviderRawDescription,
|
||||
&row.TextDescription,
|
||||
&row.DetailedText,
|
||||
&row.IconURL,
|
||||
&row.TemperatureC,
|
||||
&row.TemperatureCMin,
|
||||
&row.TemperatureCMax,
|
||||
&row.DewpointC,
|
||||
&row.RelativeHumidityPercent,
|
||||
&row.WindDirectionDegrees,
|
||||
&row.WindSpeedKmh,
|
||||
&row.WindGustKmh,
|
||||
&row.BarometricPressurePa,
|
||||
&row.VisibilityMeters,
|
||||
&row.ApparentTemperatureC,
|
||||
&row.CloudCoverPercent,
|
||||
&row.ProbabilityOfPrecipitationPercent,
|
||||
&row.PrecipitationAmountMM,
|
||||
&row.SnowfallDepthMM,
|
||||
&row.UVIndex,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("scan forecast period row: %w", err)
|
||||
}
|
||||
out = append(out, mapForecastPeriodRow(row))
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("iterate forecast period rows: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user