@@ -4,16 +4,20 @@ import (
44 "encoding/csv"
55 "fmt"
66 "io"
7+ "math"
8+ "regexp"
9+ "sort"
710 "strconv"
811 "strings"
912 "time"
1013
14+ "github.com/lauripiispanen/most-active-github-users-counter/github"
1115 "github.com/lauripiispanen/most-active-github-users-counter/top"
1216)
1317
14- type Format func (users top. GithubUsers , writer io.Writer ) error
18+ type Format func (users GithubUserList , writer io.Writer , options top. Options ) error
1519
16- func PlainOutput (users top. GithubUsers , writer io.Writer ) error {
20+ func PlainOutput (users GithubUserList , writer io.Writer , options top. Options ) error {
1721 fmt .Fprintln (writer , "USERS\n --------" )
1822 for i , user := range users {
1923 fmt .Fprintf (writer , "#%+v: %+v (%+v):%+v (%+v) %+v\n " , i + 1 , user .Name , user .Login , user .ContributionCount , user .Company , strings .Join (user .Organizations , "," ))
@@ -25,7 +29,7 @@ func PlainOutput(users top.GithubUsers, writer io.Writer) error {
2529 return nil
2630}
2731
28- func CsvOutput (users top. GithubUsers , writer io.Writer ) error {
32+ func CsvOutput (users GithubUserList , writer io.Writer , options top. Options ) error {
2933 w := csv .NewWriter (writer )
3034 if err := w .Write ([]string {"rank" , "name" , "login" , "contributions" , "company" , "organizations" }); err != nil {
3135 return err
@@ -45,12 +49,16 @@ func CsvOutput(users top.GithubUsers, writer io.Writer) error {
4549 return nil
4650}
4751
48- func YamlOutput (users top.GithubUsers , writer io.Writer ) error {
49- fmt .Fprintln (writer , "users:" )
50- for i , user := range users {
51- fmt .Fprintf (
52- writer ,
53- `
52+ func YamlOutput (users GithubUserList , writer io.Writer , options top.Options ) error {
53+ outputUsers := func (user []github.User , public_only bool ) {
54+ for i , u := range user {
55+ contributionCount := u .ContributionCount
56+ if public_only {
57+ contributionCount = u .PublicContributionCount
58+ }
59+ fmt .Fprintf (
60+ writer ,
61+ `
5462 - rank: %+v
5563 name: '%+v'
5664 login: '%+v'
@@ -59,30 +67,177 @@ func YamlOutput(users top.GithubUsers, writer io.Writer) error {
5967 company: '%+v'
6068 organizations: '%+v'
6169` ,
62- i + 1 ,
63- strings .Replace (user .Name , "'" , "''" , - 1 ),
64- strings .Replace (user .Login , "'" , "''" , - 1 ),
65- user .AvatarURL ,
66- user .ContributionCount ,
67- strings .Replace (user .Company , "'" , "''" , - 1 ),
68- strings .Replace (strings .Join (user .Organizations , "," ), "'" , "''" , - 1 ))
70+ i + 1 ,
71+ strings .Replace (u .Name , "'" , "''" , - 1 ),
72+ strings .Replace (u .Login , "'" , "''" , - 1 ),
73+ u .AvatarURL ,
74+ contributionCount ,
75+ strings .Replace (u .Company , "'" , "''" , - 1 ),
76+ strings .Replace (strings .Join (u .Organizations , "," ), "'" , "''" , - 1 ))
77+ }
6978 }
70- fmt .Fprintln (writer , "\n organizations:" )
7179
72- for i , org := range users .TopOrgs (10 ) {
73- fmt .Fprintf (
74- writer ,
75- `
80+ topPublic := users .TopPublic (options .Amount )
81+ fmt .Fprintln (writer , "public_users:" )
82+ outputUsers (topPublic , true )
83+
84+ topPrivate := users .TopPrivate (options .Amount )
85+ fmt .Fprintln (writer , "\n private_users:" )
86+ outputUsers (topPrivate , false )
87+
88+ outputOrganizations := func (orgs Organizations ) {
89+ for i , org := range orgs {
90+ fmt .Fprintf (
91+ writer ,
92+ `
7693 - rank: %+v
7794 name: '%+v'
7895 membercount: %+v
7996` ,
80- i + 1 ,
81- strings .Replace (org .Name , "'" , "''" , - 1 ),
82- org .MemberCount )
97+ i + 1 ,
98+ strings .Replace (org .Name , "'" , "''" , - 1 ),
99+ org .MemberCount )
100+ }
83101 }
84102
103+ fmt .Fprintln (writer , "\n public_organizations:" )
104+ outputOrganizations (topPublic .TopOrgs (10 ))
105+ fmt .Fprintln (writer , "\n private_organizations:" )
106+ outputOrganizations (topPrivate .TopOrgs (10 ))
107+
85108 fmt .Fprintf (writer , "generated: %+v\n " , time .Now ())
109+ fmt .Fprintf (writer , "min_followers_required: %+v\n " , users .MinFollowers ())
86110
87111 return nil
88112}
113+
114+ var companyLogin = regexp .MustCompile (`^\@([a-zA-Z0-9]+)$` )
115+
116+ func trim (users GithubUserList , numTop int ) GithubUserList {
117+ if numTop == 0 {
118+ numTop = 256
119+ }
120+ if len (users ) < numTop {
121+ numTop = len (users )
122+ }
123+ return users [:numTop ]
124+ }
125+
126+ func clone (users GithubUserList ) GithubUserList {
127+ usersCloned := make (GithubUserList , len (users ))
128+ copy (usersCloned , users )
129+ return usersCloned
130+ }
131+
132+ type GithubUserList []github.User
133+
134+ func (users GithubUserList ) TopPublic (amount int ) GithubUserList {
135+ u := TopPublicUsers (clone (users ))
136+ sort .Sort (u )
137+ return trim (GithubUserList (u ), amount )
138+ }
139+
140+ func (users GithubUserList ) TopPrivate (amount int ) GithubUserList {
141+ u := TopPrivateUsers (clone (users ))
142+ sort .Sort (u )
143+ return trim (GithubUserList (u ), amount )
144+ }
145+
146+ func (slice GithubUserList ) MinFollowers () int {
147+ if len (slice ) == 0 {
148+ return 0
149+ }
150+ followers := math .MaxInt32
151+ for _ , user := range slice {
152+ if user .FollowerCount < followers {
153+ followers = user .FollowerCount
154+ }
155+ }
156+ return followers
157+ }
158+
159+ type TopPublicUsers GithubUserList
160+
161+ func (slice TopPublicUsers ) Len () int {
162+ return len (slice )
163+ }
164+
165+ func (slice TopPublicUsers ) Less (i , j int ) bool {
166+ return slice [i ].PublicContributionCount > slice [j ].PublicContributionCount
167+ }
168+
169+ func (slice TopPublicUsers ) Swap (i , j int ) {
170+ slice [i ], slice [j ] = slice [j ], slice [i ]
171+ }
172+
173+ type TopPrivateUsers GithubUserList
174+
175+ func (slice TopPrivateUsers ) Len () int {
176+ return len (slice )
177+ }
178+
179+ func (slice TopPrivateUsers ) Less (i , j int ) bool {
180+ return slice [i ].ContributionCount > slice [j ].ContributionCount
181+ }
182+
183+ func (slice TopPrivateUsers ) Swap (i , j int ) {
184+ slice [i ], slice [j ] = slice [j ], slice [i ]
185+ }
186+
187+ type Organization struct {
188+ Name string
189+ MemberCount int
190+ }
191+
192+ type Organizations []Organization
193+
194+ func (slice Organizations ) Len () int {
195+ return len (slice )
196+ }
197+
198+ func (slice Organizations ) Less (i , j int ) bool {
199+ return slice [i ].MemberCount > slice [j ].MemberCount
200+ }
201+
202+ func (slice Organizations ) Swap (i , j int ) {
203+ slice [i ], slice [j ] = slice [j ], slice [i ]
204+ }
205+
206+ func (slice GithubUserList ) TopOrgs (count int ) Organizations {
207+ orgsMap := make (map [string ]int )
208+ for _ , user := range slice {
209+ userOrgs := user .Organizations
210+ orgMatches := companyLogin .FindStringSubmatch (strings .Trim (user .Company , " " ))
211+ if len (orgMatches ) > 0 {
212+ orgLogin := companyLogin .FindStringSubmatch (strings .Trim (user .Company , " " ))[1 ]
213+ if len (orgLogin ) > 0 && ! contains (userOrgs , orgLogin ) {
214+ userOrgs = append (userOrgs , orgLogin )
215+ }
216+ }
217+
218+ for _ , o := range userOrgs {
219+ org := strings .ToLower (o )
220+ orgsMap [org ] = orgsMap [org ] + 1
221+ }
222+ }
223+
224+ orgs := Organizations {}
225+
226+ for k , v := range orgsMap {
227+ orgs = append (orgs , Organization {Name : k , MemberCount : v })
228+ }
229+ sort .Sort (orgs )
230+ if len (orgs ) > count {
231+ return orgs [:count ]
232+ }
233+ return orgs
234+ }
235+
236+ func contains (s []string , e string ) bool {
237+ for _ , a := range s {
238+ if a == e {
239+ return true
240+ }
241+ }
242+ return false
243+ }
0 commit comments