Live video on the AT Protocol
at eli/database-resync 162 lines 3.2 kB view raw
1package model 2 3import ( 4 "database/sql" 5 "encoding/json" 6 "fmt" 7 "math" 8 "time" 9 10 "github.com/google/uuid" 11 "gorm.io/datatypes" 12) 13 14type PlayerEventAPI struct { 15 ID string `json:"id"` 16 Time time.Time `json:"time"` 17 PlayerID string `json:"playerId"` 18 EventType string `json:"eventType"` 19 Meta map[string]any `json:"meta"` 20} 21 22type PlayerEvent struct { 23 ID string `gorm:"primarykey"` 24 Time time.Time 25 PlayerID string `gorm:"index"` 26 EventType string 27 Meta datatypes.JSON 28} 29 30func MaybeNull(s string) sql.NullString { 31 if s == "" { 32 return sql.NullString{Valid: false} 33 } 34 return sql.NullString{Valid: true, String: s} 35} 36 37func (m *DBModel) CreatePlayerEvent(event PlayerEventAPI) error { 38 uu, err := uuid.NewV7() 39 if err != nil { 40 return err 41 } 42 metaBs, err := json.Marshal(event.Meta) 43 if err != nil { 44 return err 45 } 46 err = m.DB.Model(PlayerEvent{}).Create(PlayerEvent{ 47 ID: uu.String(), 48 Time: event.Time, 49 PlayerID: event.PlayerID, 50 EventType: event.EventType, 51 Meta: datatypes.JSON(metaBs), 52 }).Error 53 if err != nil { 54 return err 55 } 56 return nil 57} 58 59func (m *DBModel) ListPlayerEvents(playerID string) ([]PlayerEvent, error) { 60 events := []PlayerEvent{} 61 // err := m.DB.Find(&events).Error 62 err := m.DB.Where("player_id = ?", playerID).Find(&events).Error 63 if err != nil { 64 return nil, fmt.Errorf("error retrieving player events: %w", err) 65 } 66 return events, nil 67} 68 69func (m *DBModel) PlayerReport(playerID string) (map[string]any, error) { 70 events, err := m.ListPlayerEvents(playerID) 71 if err != nil { 72 return nil, err 73 } 74 whatHappenedReport := map[string]float64{} 75 retries := 0 76 for _, e := range events { 77 if e.EventType == "retry" { 78 retries++ 79 continue 80 } 81 if e.EventType != "aq-played" { 82 continue 83 } 84 bs, err := e.Meta.MarshalJSON() 85 if err != nil { 86 return nil, err 87 } 88 meta := map[string]any{} 89 err = json.Unmarshal(bs, &meta) 90 if err != nil { 91 return nil, err 92 } 93 whatHappenedAny, ok := meta["whatHappened"] 94 if !ok { 95 continue 96 } 97 whatHappened, ok := whatHappenedAny.(map[string]any) 98 if !ok { 99 continue 100 } 101 for state, time := range whatHappened { 102 ms, ok := time.(float64) 103 if ok { 104 whatHappenedReport[state] = whatHappenedReport[state] + ms 105 } 106 } 107 } 108 109 avSyncs := []float64{} 110 for _, e := range events { 111 if e.EventType != "av-sync" { 112 continue 113 } 114 bs, err := e.Meta.MarshalJSON() 115 if err != nil { 116 return nil, err 117 } 118 meta := map[string]any{} 119 err = json.Unmarshal(bs, &meta) 120 if err != nil { 121 return nil, err 122 } 123 diff, ok := meta["diff"].(float64) 124 if !ok { 125 continue 126 } 127 avSyncs = append(avSyncs, diff) 128 } 129 130 report := map[string]any{ 131 "whatHappened": whatHappenedReport, 132 } 133 134 if len(avSyncs) > 0 { 135 min := math.Inf(1) 136 max := math.Inf(-1) 137 sum := 0.0 138 for _, sync := range avSyncs { 139 if sync < min { 140 min = sync 141 } 142 if sync > max { 143 max = sync 144 } 145 sum += sync 146 } 147 avg := sum / float64(len(avSyncs)) 148 report["avSync"] = map[string]float64{ 149 "min": min, 150 "max": max, 151 "avg": avg, 152 } 153 } 154 155 report["retries"] = retries 156 157 return report, nil 158} 159 160func (m *DBModel) ClearPlayerEvents() error { 161 return m.DB.Where("1 = 1").Delete(&PlayerEvent{}).Error 162}