feeds.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package traffic
  2. import (
  3. "apiote.xyz/p/szczanieckiej/config"
  4. "apiote.xyz/p/szczanieckiej/file"
  5. "fmt"
  6. "net/http"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "time"
  11. "golang.org/x/text/language"
  12. "golang.org/x/text/transform"
  13. "git.sr.ht/~sircmpwn/go-bare"
  14. )
  15. func LastUpdate(feedsPath, feedID string) (time.Time, error) {
  16. updatesFilename := filepath.Join(feedsPath, "updated.bare")
  17. updatesFile, err := os.Open(updatesFilename)
  18. if err != nil {
  19. return time.Time{}, fmt.Errorf("while opening updates file: %w", err)
  20. }
  21. var lastUpdated map[string]string
  22. err = bare.UnmarshalReader(updatesFile, &lastUpdated)
  23. if err != nil {
  24. return time.Time{}, fmt.Errorf("while unmarshaling updates file: %w", err)
  25. }
  26. t, err := time.Parse(time.RFC3339, lastUpdated[feedID])
  27. if err != nil {
  28. return time.Time{}, fmt.Errorf("while parsing update time: %w", err)
  29. }
  30. return t, nil
  31. }
  32. type Feed interface {
  33. fmt.Stringer
  34. ConvertVehicles(string) error
  35. GetVersions(time.Time) ([]Version, error)
  36. GetLocation() *time.Location
  37. RealtimeFeeds() []string
  38. Transformer() transform.Transformer
  39. Name() string
  40. Attribution() map[language.Tag]string // SHOULD have und tag
  41. Description() map[language.Tag]string // SHOULD have und tag
  42. }
  43. func FeedPath(cfg config.Config, feed Feed) string {
  44. return filepath.Join(cfg.FeedsPath, feed.String())
  45. }
  46. func RegisterFeeds() map[string]Feed {
  47. return map[string]Feed{
  48. "poznan_ztm": ZtmPoznan{
  49. client: http.Client{
  50. Timeout: 30 * time.Second,
  51. },
  52. },
  53. "krakow_ztp": ZtpKrakow{},
  54. }
  55. }
  56. func MakeVersion(s string, location *time.Location) (Version, error) {
  57. version := Version{}
  58. versionDates := strings.Split(s, "_")
  59. if len(versionDates) != 2 {
  60. return version, fmt.Errorf("invalid version string %s, not /.*_.*/", s)
  61. }
  62. validFrom, err := time.ParseInLocation("20060102", versionDates[0], location)
  63. if err != nil {
  64. return version, fmt.Errorf("invalid first part in %s: %w", s, err)
  65. }
  66. validTill, err := time.ParseInLocation("20060102", versionDates[1], location)
  67. if err != nil {
  68. return version, fmt.Errorf("invalid second part in %s: %w", s, err)
  69. }
  70. version.ValidFrom = validFrom
  71. version.ValidTill = validTill
  72. return version, nil
  73. }
  74. func ListVersions(cfg config.Config, f Feed) ([]Version, error) {
  75. versions := []Version{}
  76. versionNames, err := file.ListVersions(FeedPath(cfg, f), f.GetLocation())
  77. if err != nil {
  78. return versions, err
  79. }
  80. for _, name := range versionNames {
  81. version, err := MakeVersion(name, f.GetLocation())
  82. if err != nil {
  83. return versions, err
  84. }
  85. versions = append(versions, version)
  86. }
  87. return versions, err
  88. }
  89. func FindValidVersions(versions []Version, now time.Time) []Version {
  90. i := 0
  91. for _, version := range versions {
  92. if version.ValidFrom.Before(now) {
  93. i++
  94. } else {
  95. break
  96. }
  97. }
  98. if i > 0 {
  99. i = i - 1
  100. }
  101. validVersions := versions[i:]
  102. return validVersions
  103. }