Author: Adam Evyčędo <git@apiote.xyz>
fully support exceptions in calendars
api/api.go | 1 api/lineResponse.go | 4 server/route_line.go | 6 traffic/access.go | 9 + traffic/convert.go | 201 +++++++++++++++++++++++++++++++------------ traffic/structs_gen.go | 20 +++
diff --git a/api/api.go b/api/api.go index b99e1b920ca2496474c0cf0af7b2b977decab01d..78919e92d0d01fb84d85c841733172fe3a732445 100644 --- a/api/api.go +++ b/api/api.go @@ -83,6 +83,7 @@ lineGraph := LineGraphV1{ Stops: make([]StopStubV1, len(graph.StopCodes)), NextNodes: graph.NextNodes, } + delete(lineGraph.NextNodes, -1) for i, code := range graph.StopCodes { stopStub, err := traffic.GetStopStub(code, line.Name, context, t) if err != nil { diff --git a/api/lineResponse.go b/api/lineResponse.go index 1fda5e76233deed002c02e15e432010dfac279c9..76db96c1e13c112f22038f75dbd8f17f3b1e0888 100644 --- a/api/lineResponse.go +++ b/api/lineResponse.go @@ -1,6 +1,8 @@ package api -import "apiote.xyz/p/szczanieckiej/traffic" +import ( + "apiote.xyz/p/szczanieckiej/traffic" +) func MakeLineResponse(line traffic.Line, context traffic.Context, t *traffic.Traffic, accept uint) (LineResponse, error) { switch accept { diff --git a/server/route_line.go b/server/route_line.go index 0a9ad73e5d7078fd3dd552363e4610e6d8e606b5..2e58663b43be11cc0dc47a85b0549d6eae7f82be 100644 --- a/server/route_line.go +++ b/server/route_line.go @@ -82,12 +82,13 @@ r: r, t: t, c: cfg, a: accept, + f: feedName, }, } result := gott.R[AbstractHandlerVars]{ S: handlerVars, } - result. + result = result. Map(splitPath). Tee(checkLinePath). Bind(getVersionCode). @@ -95,7 +96,8 @@ Map(createContext). Bind(getLine). Tee(checkEmptyLine). Bind(makeLineResponse). - Bind(marshalLineResponse) + Bind(marshalLineResponse). + Tee(writeResponse) return result.E } diff --git a/traffic/access.go b/traffic/access.go index 375544ffead8855c21aaafb24ad23f84b1160c30..51113da428bb4cf1873c2bf6727a84c8129873fd 100644 --- a/traffic/access.go +++ b/traffic/access.go @@ -85,9 +85,12 @@ schedules := map[string]struct{}{} weekday := uint8(1 << time.Weekday()) date := time.Format(DateFormat) for _, schedule := range calendar { - if schedule.StartDate <= date && date <= schedule.EndDate && - (schedule.Weekdays&weekday != 0) { - schedules[schedule.Id] = struct{}{} + for _, dateRange := range schedule.DateRanges { + if dateRange.Start <= date && date <= dateRange.End && + (dateRange.Weekdays&weekday != 0) { + schedules[schedule.Id] = struct{}{} + break + } } } var err error diff --git a/traffic/convert.go b/traffic/convert.go index a49e3b03356915983b357d580834a3bc90b1e9b0..5c58ca183b0b3f002d67bae405fc2360f46ee203 100644 --- a/traffic/convert.go +++ b/traffic/convert.go @@ -83,6 +83,7 @@ ValidTillError []error tripHeadsigns map[string]string stopNames map[string]string feedInfo FeedInfo + schedules map[string]Schedule } // helper functions @@ -285,8 +286,8 @@ return c, e } func convertCalendar(c feedConverter) (feedConverter, error) { // ( feedInfo -- >> calendar) + c.schedules = map[string]Schedule{} path := c.TmpFeedPath - resultFile := c.TrafficCalendarFile calendarFile, err := os.Open(filepath.Join(path, "calendar.txt")) if err != nil { return c, fmt.Errorf("while opening file: %w", err) @@ -315,40 +316,37 @@ } schedule.Id = record[fields["service_id"]] startDate := record[fields["start_date"]] endDate := record[fields["end_date"]] - schedule.StartDate = startDate[:4] + "-" + startDate[4:6] + "-" + startDate[6:] - schedule.EndDate = endDate[:4] + "-" + endDate[4:6] + "-" + endDate[6:] + schedule.DateRanges = []DateRange{ + DateRange{ + Start: startDate[:4] + "-" + startDate[4:6] + "-" + startDate[6:], + End: endDate[:4] + "-" + endDate[4:6] + "-" + endDate[6:], + }, + } if record[fields["monday"]] == "1" { - schedule.Weekdays |= (1 << 1) + schedule.DateRanges[0].Weekdays |= (1 << 1) } if record[fields["tuesday"]] == "1" { - schedule.Weekdays |= (1 << 2) + schedule.DateRanges[0].Weekdays |= (1 << 2) } if record[fields["wednesday"]] == "1" { - schedule.Weekdays |= (1 << 3) + schedule.DateRanges[0].Weekdays |= (1 << 3) } if record[fields["thursday"]] == "1" { - schedule.Weekdays |= (1 << 4) + schedule.DateRanges[0].Weekdays |= (1 << 4) } if record[fields["friday"]] == "1" { - schedule.Weekdays |= (1 << 5) + schedule.DateRanges[0].Weekdays |= (1 << 5) } if record[fields["saturday"]] == "1" { - schedule.Weekdays |= (1 << 6) + schedule.DateRanges[0].Weekdays |= (1 << 6) } if record[fields["sunday"]] == "1" { - schedule.Weekdays |= (1 << 0) - schedule.Weekdays |= (1 << 7) - } - bytes, err := bare.Marshal(&schedule) - if err != nil { - return c, fmt.Errorf("while marshalling: %w", err) + schedule.DateRanges[0].Weekdays |= (1 << 0) + schedule.DateRanges[0].Weekdays |= (1 << 7) } - _, err = resultFile.Write(bytes) - if err != nil { - return c, fmt.Errorf("while writing: %w", err) - } + c.schedules[schedule.Id] = schedule - scheduleStart, err := time.ParseInLocation("2006-01-02", schedule.StartDate, c.Feed.GetLocation()) + scheduleStart, err := time.ParseInLocation("2006-01-02", schedule.DateRanges[0].Start, c.Feed.GetLocation()) if err != nil { c.ValidFromError = append(c.ValidFromError, err) } @@ -357,7 +355,7 @@ c.ValidFrom = scheduleStart c.feedInfo.ValidSince = scheduleStart.Format("20060102") } - scheduleEnd, err := time.ParseInLocation("2006-01-02", schedule.EndDate, c.Feed.GetLocation()) + scheduleEnd, err := time.ParseInLocation("2006-01-02", schedule.DateRanges[0].End, c.Feed.GetLocation()) if err != nil { c.ValidTillError = append(c.ValidTillError, err) } @@ -371,7 +369,6 @@ } func convertCalendarDates(c feedConverter) (feedConverter, error) { path := c.TmpFeedPath - resultFile := c.TrafficCalendarFile datesFile, err := os.Open(filepath.Join(path, "calendar_dates.txt")) if err != nil { return c, fmt.Errorf("while opening file: %w", err) @@ -389,7 +386,6 @@ fields[headerField] = i } for { - schedule := Schedule{} record, err := r.Read() if err == io.EOF { break @@ -398,42 +394,132 @@ if err != nil { return c, fmt.Errorf("while reading a record: %w", err) } if record[fields["exception_type"]] == "1" { + id := record[fields["service_id"]] + schedule := c.schedules[id] + date := record[fields["date"]] - schedule.Id = record[fields["service_id"]] - schedule.StartDate = date[:4] + "-" + date[4:6] + "-" + date[6:] - schedule.EndDate = date[:4] + "-" + date[4:6] + "-" + date[6:] - schedule.Weekdays = 0xff + dateRange := DateRange{ + Start: date[:4] + "-" + date[4:6] + "-" + date[6:], + End: date[:4] + "-" + date[4:6] + "-" + date[6:], + Weekdays: 0xff, + } + if len(schedule.DateRanges) == 0 { + schedule.Id = id + schedule.DateRanges = []DateRange{dateRange} + } else { + inserted := false + for i, dr := range schedule.DateRanges { + if dateRange.Start < dr.Start { + newDateRanges := append(schedule.DateRanges[:i], dateRange) + newDateRanges = append(newDateRanges, schedule.DateRanges[i:]...) + schedule.DateRanges = newDateRanges + inserted = true + break + } + } + if !inserted { + schedule.DateRanges = append(schedule.DateRanges, dateRange) + } + } + + c.schedules[schedule.Id] = schedule + + scheduleStart, err := time.ParseInLocation("2006-01-02", schedule.DateRanges[0].Start, c.Feed.GetLocation()) + if err != nil { + c.ValidFromError = append(c.ValidFromError, err) + } + if err == nil && (c.ValidFrom.IsZero() || scheduleStart.Before(c.ValidFrom)) { + c.ValidFrom = scheduleStart + c.feedInfo.ValidSince = scheduleStart.Format("20060102") + } + + scheduleEnd, err := time.ParseInLocation("2006-01-02", schedule.DateRanges[0].End, c.Feed.GetLocation()) + if err != nil { + c.ValidTillError = append(c.ValidTillError, err) + } + if err == nil && (c.ValidTill.IsZero() || scheduleEnd.After(c.ValidTill)) { + c.ValidTill = scheduleEnd + c.feedInfo.ValidTill = scheduleEnd.Format("20060102") + } } else { - continue + date := record[fields["date"]] + formatedDate := date[:4] + "-" + date[4:6] + "-" + date[6:] + scheduleToEdit := c.schedules[record[fields["service_id"]]] + + newDateRanges := []DateRange{} + for i := 0; i < len(scheduleToEdit.DateRanges); i++ { + dateRange := scheduleToEdit.DateRanges[i] + + if dateRange.Start == formatedDate { + d, _ := time.ParseInLocation("2006-01-02", dateRange.Start, c.Feed.GetLocation()) + dateRange.Start = d.AddDate(0, 0, 1).Format("2006-01-02") + if dateRange.Start <= dateRange.End { + newDateRanges = append(newDateRanges, dateRange) + } + continue + } + + if dateRange.Start < formatedDate && formatedDate < dateRange.End { + d, _ := time.ParseInLocation("2006-01-02", formatedDate, c.Feed.GetLocation()) + range1 := DateRange{dateRange.Start, d.AddDate(0, 0, -1).Format("2006-01-02"), dateRange.Weekdays} + range2 := DateRange{d.AddDate(0, 0, 1).Format("2006-01-02"), dateRange.End, dateRange.Weekdays} + newDateRanges = append(newDateRanges, range1) + newDateRanges = append(newDateRanges, range2) + continue + } + + if formatedDate == dateRange.End { + d, _ := time.ParseInLocation("2006-01-02", dateRange.End, c.Feed.GetLocation()) + dateRange.End = d.AddDate(0, 0, -1).Format("2006-01-02") + newDateRanges = append(newDateRanges, dateRange) + continue + } + + newDateRanges = append(newDateRanges, dateRange) + } + + scheduleToEdit.DateRanges = newDateRanges + c.schedules[record[fields["service_id"]]] = scheduleToEdit } + + } + return c, nil +} + +func checkAnyCalendarConverted(c feedConverter) error { + if len(c.schedules) == 0 { + return fmt.Errorf("no calendar converted") + } + return nil +} + +func saveSchedules(c feedConverter) error { + resultFile := c.TrafficCalendarFile + + schedulesArray := make([]Schedule, len(c.schedules)) + i := 0 + for _, schedule := range c.schedules { + schedulesArray[i] = schedule + i++ + } + sort.Slice(schedulesArray, func(i, j int) bool { + return schedulesArray[i].DateRanges[0].Start < schedulesArray[j].DateRanges[0].Start + }) + + for _, schedule := range schedulesArray { bytes, err := bare.Marshal(&schedule) if err != nil { - return c, fmt.Errorf("while marshalling: %w", err) + return fmt.Errorf("while marshalling: %w", err) } _, err = resultFile.Write(bytes) if err != nil { - return c, fmt.Errorf("while writing: %w", err) + return fmt.Errorf("while writing: %w", err) } + } - scheduleStart, err := time.ParseInLocation("2006-01-02", schedule.StartDate, c.Feed.GetLocation()) - if err != nil { - c.ValidFromError = append(c.ValidFromError, err) - } - if err == nil && (c.ValidFrom.IsZero() || scheduleStart.Before(c.ValidFrom)) { - c.ValidFrom = scheduleStart - c.feedInfo.ValidSince = scheduleStart.Format("20060102") - } + c.schedules = map[string]Schedule{} - scheduleEnd, err := time.ParseInLocation("2006-01-02", schedule.EndDate, c.Feed.GetLocation()) - if err != nil { - c.ValidTillError = append(c.ValidTillError, err) - } - if err == nil && (c.ValidTill.IsZero() || scheduleEnd.After(c.ValidTill)) { - c.ValidTill = scheduleEnd - c.feedInfo.ValidTill = scheduleEnd.Format("20060102") - } - } - return c, nil + return nil } func saveFeedInfo(c feedConverter) error { @@ -454,14 +540,6 @@ return fmt.Errorf("while writing: %w", err) } return nil -} - -func checkAnyCalendarConverted(c feedConverter) error { - info, err := c.TrafficCalendarFile.Stat() - if err == nil && info.Size() == 0 { - return fmt.Errorf("no calendar converted") - } - return err } func closeTrafficCalendarFile(c feedConverter, e error) (feedConverter, error) { @@ -946,7 +1024,15 @@ graph.StopCodes = append(graph.StopCodes, stop.Code) } if previousTripID != tripID { // first of current trip - graph.NextNodes[-1] = append(graph.NextNodes[-1], current) + connectionDone := false + for _, n := range graph.NextNodes[-1] { + if n == current { + connectionDone = true + } + } + if !connectionDone { + graph.NextNodes[-1] = append(graph.NextNodes[-1], current) + } } else { // second <- first to last <- penultimate of current trip connectionDone := false @@ -1337,6 +1423,7 @@ Recover(recoverCalendar). Bind(convertCalendarDates). Recover(recoverCalendar). Tee(checkAnyCalendarConverted). + Tee(saveSchedules). Tee(saveFeedInfo). Recover(closeTrafficCalendarFile). Bind(convertDepartures). diff --git a/traffic/structs_gen.go b/traffic/structs_gen.go index 9bd9ff49d7174901352a521fdee6cb3beefa02d5..bfd9eefced8b3b4b7452e7e0991ea2a21657ca9c 100644 --- a/traffic/structs_gen.go +++ b/traffic/structs_gen.go @@ -223,10 +223,8 @@ return bare.Marshal(t) } type Schedule struct { - Id string `bare:"id"` - Weekdays uint8 `bare:"weekdays"` - StartDate string `bare:"startDate"` - EndDate string `bare:"endDate"` + Id string `bare:"id"` + DateRanges []DateRange `bare:"dateRanges"` } func (t *Schedule) Decode(data []byte) error { @@ -234,6 +232,20 @@ return bare.Unmarshal(data, t) } func (t *Schedule) Encode() ([]byte, error) { + return bare.Marshal(t) +} + +type DateRange struct { + Start string `bare:"start"` + End string `bare:"end"` + Weekdays uint8 `bare:"weekdays"` +} + +func (t *DateRange) Decode(data []byte) error { + return bare.Unmarshal(data, t) +} + +func (t *DateRange) Encode() ([]byte, error) { return bare.Marshal(t) }