Files
pinned_message/internal/services/schedule_parser/service.go
T
2026-05-03 23:13:27 +07:00

166 lines
4.3 KiB
Go

package schedule_parser
import (
"context"
"log"
"pinned_message/internal/models"
"pinned_message/internal/modules/data_parser"
"pinned_message/internal/modules/date_parser"
"pinned_message/internal/services/schedule_storage"
"sort"
"strings"
"time"
)
type performance struct {
Date string `csv:"ДАТА"`
Day string `csv:"день недели"`
Name string `csv:"НАЗВАНИЕ"`
Place string `csv:"МЕСТО"`
TimeCollection string `csv:"время СБОРА (для концерта)"`
TimeStart string `csv:"время НАЧАЛА"`
Numbers string `csv:"ЧТО ТАНЦУЕМ"`
Costumes string `csv:"КОСТЮМЫ"`
}
type ScheduleParser struct {
dataParser data_parser.IDataParser
dateParser date_parser.IDateParser
scheduleStorage *schedule_storage.ScheduleStorage
}
func NewScheduleParser(
dataParser data_parser.IDataParser,
dateParser date_parser.IDateParser,
scheduleStorage *schedule_storage.ScheduleStorage,
) *ScheduleParser {
return &ScheduleParser{
dataParser: dataParser,
dateParser: dateParser,
scheduleStorage: scheduleStorage,
}
}
func (p *ScheduleParser) Run(ctx context.Context) {
ticker := time.NewTicker(30 * time.Minute)
defer ticker.Stop()
sheetURL := "https://docs.google.com/spreadsheets/d/1v57bCAG764j1ULXDMb3amNFMzkkLmObKWsl5oE0Xq00/edit?gid=57461713#gid=57461713"
p.parseAndSaveSchedule(ctx, sheetURL)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
p.parseAndSaveSchedule(ctx, sheetURL)
}
}
}
func (p *ScheduleParser) parseAndSaveSchedule(ctx context.Context, sheetURL string) {
schedule, err := p.parseSchedule(ctx, sheetURL)
if err != nil {
log.Printf("Error parse schedule: %s\n", sheetURL)
return
}
if err := p.scheduleStorage.SaveSchedule(schedule); err != nil {
log.Printf("Error save err: %s schedule: %s\n", err, sheetURL)
}
}
func (p *ScheduleParser) parseSchedule(ctx context.Context, sheetURL string) (*models.Schedule, error) {
var performances []performance
if err := p.dataParser.Parse(ctx, sheetURL, &performances); err != nil {
return nil, err
}
return p.mapSchedule(performances), nil
}
func (p *ScheduleParser) mapSchedule(performances []performance) *models.Schedule {
days := map[time.Time]*models.Day{}
var currentDate time.Time
var prevDate time.Time
for _, performance := range performances {
if performance.Name == "" || performance.Name == "-" {
continue
}
if performance.Date != "" {
date, err := p.mapDate(performance.Date)
if err != nil {
panic(err)
}
currentDate = date
}
if performance.Date == "" {
currentDate = prevDate
}
if _, ok := days[currentDate]; !ok {
days[currentDate] = &models.Day{
Date: currentDate,
}
}
currentDay := days[currentDate]
currentDay.Performances = append(
currentDay.Performances,
&models.DayPerformance{
TimeCollection: performance.TimeCollection,
TimeStart: performance.TimeStart,
Place: performance.Place,
Name: performance.Name,
Numbers: p.mapNumbers(performance.Numbers),
Costumes: performance.Costumes,
},
)
prevDate = currentDate
}
return &models.Schedule{
UpdateTime: time.Now().String(),
Days: p.mapToSortedArray(days),
}
}
func (p *ScheduleParser) mapDate(date string) (time.Time, error) {
if date == "" {
return time.Time{}, nil
}
return p.dateParser.Parse(date)
}
func (p *ScheduleParser) mapNumbers(numbers string) []*models.Number {
// вкусовщина и костыли
numbers = strings.ReplaceAll(numbers, "Ай, да русская душа", "Ай да русская душа")
numbers = strings.ReplaceAll(numbers, "«", "\"")
numbers = strings.ReplaceAll(numbers, "»", "\"")
numbers = strings.ReplaceAll(numbers, "”", "\"")
names := strings.Split(numbers, ",")
res := make([]*models.Number, 0, len(names))
for _, name := range names {
res = append(
res,
&models.Number{
Name: name,
},
)
}
return res
}
func (p *ScheduleParser) mapToSortedArray(m map[time.Time]*models.Day) []*models.Day {
result := make([]*models.Day, 0, len(m))
for _, day := range m {
result = append(result, day)
}
sort.Slice(result, func(i, j int) bool {
return result[i].Date.Before(result[j].Date)
})
return result
}