this repo has no description
1package db 2 3import ( 4 "sort" 5 "time" 6) 7 8type TimelineEvent struct { 9 *Repo 10 *Follow 11 *Star 12 13 EventAt time.Time 14 15 // optional: populate only if Repo is a fork 16 Source *Repo 17 18 // optional: populate only if event is Follow 19 *Profile 20 *FollowStats 21} 22 23type FollowStats struct { 24 Followers int 25 Following int 26} 27 28const Limit = 50 29 30// TODO: this gathers heterogenous events from different sources and aggregates 31// them in code; if we did this entirely in sql, we could order and limit and paginate easily 32func MakeTimeline(e Execer) ([]TimelineEvent, error) { 33 var events []TimelineEvent 34 35 repos, err := getTimelineRepos(e) 36 if err != nil { 37 return nil, err 38 } 39 40 stars, err := getTimelineStars(e) 41 if err != nil { 42 return nil, err 43 } 44 45 follows, err := getTimelineFollows(e) 46 if err != nil { 47 return nil, err 48 } 49 50 events = append(events, repos...) 51 events = append(events, stars...) 52 events = append(events, follows...) 53 54 sort.Slice(events, func(i, j int) bool { 55 return events[i].EventAt.After(events[j].EventAt) 56 }) 57 58 // Limit the slice to 100 events 59 if len(events) > Limit { 60 events = events[:Limit] 61 } 62 63 return events, nil 64} 65 66func getTimelineRepos(e Execer) ([]TimelineEvent, error) { 67 repos, err := GetRepos(e, Limit) 68 if err != nil { 69 return nil, err 70 } 71 72 // fetch all source repos 73 var args []string 74 for _, r := range repos { 75 if r.Source != "" { 76 args = append(args, r.Source) 77 } 78 } 79 80 var origRepos []Repo 81 if args != nil { 82 origRepos, err = GetRepos(e, 0, FilterIn("at_uri", args)) 83 } 84 if err != nil { 85 return nil, err 86 } 87 88 uriToRepo := make(map[string]Repo) 89 for _, r := range origRepos { 90 uriToRepo[r.RepoAt().String()] = r 91 } 92 93 var events []TimelineEvent 94 for _, r := range repos { 95 var source *Repo 96 if r.Source != "" { 97 if origRepo, ok := uriToRepo[r.Source]; ok { 98 source = &origRepo 99 } 100 } 101 102 events = append(events, TimelineEvent{ 103 Repo: &r, 104 EventAt: r.Created, 105 Source: source, 106 }) 107 } 108 109 return events, nil 110} 111 112func getTimelineStars(e Execer) ([]TimelineEvent, error) { 113 stars, err := GetStars(e, Limit) 114 if err != nil { 115 return nil, err 116 } 117 118 // filter star records without a repo 119 n := 0 120 for _, s := range stars { 121 if s.Repo != nil { 122 stars[n] = s 123 n++ 124 } 125 } 126 stars = stars[:n] 127 128 var events []TimelineEvent 129 for _, s := range stars { 130 events = append(events, TimelineEvent{ 131 Star: &s, 132 EventAt: s.Created, 133 }) 134 } 135 136 return events, nil 137} 138 139func getTimelineFollows(e Execer) ([]TimelineEvent, error) { 140 follows, err := GetFollows(e, Limit) 141 if err != nil { 142 return nil, err 143 } 144 145 var subjects []string 146 for _, f := range follows { 147 subjects = append(subjects, f.SubjectDid) 148 } 149 150 if subjects == nil { 151 return nil, nil 152 } 153 154 profiles, err := GetProfiles(e, FilterIn("did", subjects)) 155 if err != nil { 156 return nil, err 157 } 158 159 followStatMap := make(map[string]FollowStats) 160 for _, s := range subjects { 161 followers, following, err := GetFollowerFollowingCount(e, s) 162 if err != nil { 163 return nil, err 164 } 165 followStatMap[s] = FollowStats{ 166 Followers: followers, 167 Following: following, 168 } 169 } 170 171 var events []TimelineEvent 172 for _, f := range follows { 173 profile, _ := profiles[f.SubjectDid] 174 followStatMap, _ := followStatMap[f.SubjectDid] 175 176 events = append(events, TimelineEvent{ 177 Follow: &f, 178 Profile: profile, 179 FollowStats: &followStatMap, 180 EventAt: f.FollowedAt, 181 }) 182 } 183 184 return events, nil 185}