GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( c9b528...85ea38 )
by Fedir
02:26
created

main.main   D

Complexity

Conditions 11

Size

Total Lines 96
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 85
nop 0
dl 0
loc 96
rs 4.2709
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like main.main often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// Copyright 2018 Fedir RYKHTIK. All rights reserved.
2
// Use of this source code is governed by the GNU GPL 3.0
3
// license that can be found in the LICENSE file.
4
5
// ghstat - statistical multi-criteria decision-making comparator for Github's projects.
6
package main
7
8
import (
9
	"encoding/csv"
10
	"encoding/json"
11
	"flag"
12
	"fmt"
13
	"log"
14
	"os"
15
	"path/filepath"
16
	"strings"
17
	"time"
18
19
	"github.com/fedir/ghstat/github"
20
	"github.com/fedir/ghstat/httpcache"
21
	"github.com/fedir/ghstat/timing"
22
)
23
24
func main() {
25
	var (
26
		clearHTTPCache         = flag.Bool("cc", false, "Clear HTTP cache")
27
		clearHTTPCacheDryRun   = flag.Bool("ccdr", false, "Clear HTTP cache (dry run)")
28
		debug                  = flag.Bool("d", false, "Debug mode")
29
		resultFileSavePath     = flag.String("f", "", "File path where result CSV file will be saved")
30
		rateLimitCheck         = flag.Bool("l", false, "Rate limit check")
31
		repositoriesKeysManual = flag.String("r", "", "Repositories keys")
32
		tmpFolder              = flag.String("t", "test_data", "Clear HTTP cache (dry run)")
33
		repositoriesKeys       = []string{}
34
	)
35
	flag.Parse()
36
	if *clearHTTPCache || *clearHTTPCacheDryRun {
37
		clearHTTPCacheFolder(*tmpFolder, *clearHTTPCacheDryRun)
38
		os.Exit(0)
39
	}
40
	if *rateLimitCheck {
41
		checkAndPrintRateLimit()
42
		os.Exit(0)
43
	}
44
	if *rateLimitCheck {
45
		checkAndPrintRateLimit()
46
		os.Exit(0)
47
	}
48
	if *repositoriesKeysManual != "" {
49
		repositoriesKeys = strings.Split(*repositoriesKeysManual, ",")
50
	} else {
51
		repositoriesKeys = []string{
52
			"astaxie/beego",
53
			"gohugoio/hugo",
54
			"gin-gonic/gin",
55
			"labstack/echo",
56
			"revel/revel",
57
			"gobuffalo/buffalo",
58
			"go-chi/chi",
59
			"kataras/iris",
60
		}
61
	}
62
	csvFilePath := ""
63
	if *resultFileSavePath != "" {
64
		csvFilePath = *resultFileSavePath
65
	} else {
66
		csvFilePath = "result.csv"
67
	}
68
	var ghData = [][]string{}
69
	headers := []string{
70
		"Name",
71
		"URL",
72
		"Author",
73
		"Author's followers",
74
		"Top 10 contributors followers",
75
		"Created at",
76
		"Age in days",
77
		"Total commits",
78
		"Total additions",
79
		"Total deletions",
80
		"Total code changes",
81
		"Last commit date",
82
		"Medium commit size",
83
		"Stargazers",
84
		"Forks",
85
		"Contributors",
86
		"Active forkers, %",
87
		"Open issues",
88
		"Total issues",
89
		"Issue/day",
90
		"Closed issues, %",
91
		"Place",
92
	}
93
	ghDataColumnIndexes := map[string]int{
94
		"nameColumn":                       0,
95
		"authorsFollowersColumn":           3,
96
		"top10ContributorsFollowersColumn": 4,
97
		"totalAdditionsColumn":             8,
98
		"ageColumn":                        6,
99
		"totalCommitsColumn":               7,
100
		"totalDeletionsColumn":             9,
101
		"totalCodeChangesColumn":           10,
102
		"lastCommitDate":                   11,
103
		"mediCommitSizeColumn":             12,
104
		"stargazersColumn":                 13,
105
		"activeForkersColumn":              16,
106
		"issuesByDayColumn":                19,
107
		"closedIssuesPercentageColumn":     20,
108
		"totalPointsColumnIndex":           21,
109
	}
110
	dataChan := make(chan []string, len(repositoriesKeys))
111
	for _, rKey := range repositoriesKeys {
112
		go fillRepositoryStatistics(rKey, *tmpFolder, *debug, dataChan)
113
	}
114
	for range repositoriesKeys {
115
		ghData = append(ghData, <-dataChan)
116
	}
117
	greetings := rateGhData(ghData, ghDataColumnIndexes)
118
	fmt.Println(greetings)
119
	writeCsv(csvFilePath, headers, ghData)
120
}
121
122
func fillRepositoryStatistics(rKey string, tmpFolder string, debug bool, dataChan chan []string) {
123
	repositoryData := github.GetRepositoryStatistics(rKey, tmpFolder, debug)
124
	repositoryAge := int(time.Since(repositoryData.CreatedAt).Seconds() / 86400)
125
	authorLogin, lastCommitDate := github.GetRepositoryCommitsData(rKey, tmpFolder, debug)
126
	authorFollowers := 0
127
	if authorLogin != "" {
128
		authorFollowers = github.GetUserFollowers(authorLogin, tmpFolder, debug)
129
	}
130
	closedIssues := github.GetRepositoryClosedIssues(rKey, tmpFolder, debug)
131
	topContributorsFollowers, totalContributors := github.GetRepositoryContributors(rKey, tmpFolder, debug)
132
	activeForkersPercentage := github.GetActiveForkersPercentage(totalContributors, repositoryData.Forks)
133
	issueByDay := github.GetIssueByDay(closedIssues+repositoryData.OpenIssues, repositoryAge)
134
	closedIssuesPercentage := github.GetClosedIssuesPercentage(repositoryData.OpenIssues, int(closedIssues))
135
	contributionStatistics := github.GetContributionStatistics(rKey, tmpFolder, debug)
136
	ghProjectData := []string{
137
		repositoryData.FullName,
138
		fmt.Sprintf("https://github.com/%s", repositoryData.FullName),
139
		fmt.Sprintf("%s", func(a string) string {
140
			if a == "" {
141
				a = "[Account removed]"
142
			}
143
			return a
144
		}(authorLogin)),
145
		fmt.Sprintf("%d", authorFollowers),
146
		fmt.Sprintf("%d", topContributorsFollowers),
147
		fmt.Sprintf("%d/%02d", repositoryData.CreatedAt.Year(), repositoryData.CreatedAt.Month()),
148
		fmt.Sprintf("%d", repositoryAge),
149
		fmt.Sprintf("%d", contributionStatistics.TotalCommits),
150
		fmt.Sprintf("%d", contributionStatistics.TotalAdditions),
151
		fmt.Sprintf("%d", contributionStatistics.TotalDeletions),
152
		fmt.Sprintf("%d", contributionStatistics.TotalCodeChanges),
153
		fmt.Sprintf(lastCommitDate.Format("2006-01-02 15:04:05")),
0 ignored issues
show
introduced by
can't check non-constant format in call to Sprintf
Loading history...
154
		fmt.Sprintf("%d", contributionStatistics.MediumCommitSize),
155
		fmt.Sprintf("%d", repositoryData.Watchers),
156
		fmt.Sprintf("%d", repositoryData.Forks),
157
		fmt.Sprintf("%d", totalContributors),
158
		fmt.Sprintf("%.2f", activeForkersPercentage),
159
		fmt.Sprintf("%d", repositoryData.OpenIssues),
160
		fmt.Sprintf("%d", closedIssues+repositoryData.OpenIssues),
161
		fmt.Sprintf("%.4f", issueByDay),
162
		fmt.Sprintf("%.2f", closedIssuesPercentage),
163
		"0",
164
	}
165
	dataChan <- ghProjectData
166
}
167
168
func clearHTTPCacheFolder(tmpFolderPath string, dryRun bool) error {
169
	d, err := os.Open(tmpFolderPath)
170
	if err != nil {
171
		log.Fatalf("Could not open %s", tmpFolderPath)
172
	}
173
	defer d.Close()
174
	names, err := d.Readdirnames(-1)
175
	if err != nil {
176
		log.Fatalf("Could not read from %s", tmpFolderPath)
177
	}
178
	for _, name := range names {
179
		fp := filepath.Join(tmpFolderPath, name)
180
		if dryRun {
181
			fmt.Printf("Deleted %s\n", fp)
182
		} else {
183
			err = os.RemoveAll(fp)
184
			if err != nil {
185
				log.Fatalf("Could not remove %s", fp)
186
			}
187
			fmt.Printf("Deleted %s\n", fp)
188
		}
189
	}
190
	return nil
191
}
192
193
func checkAndPrintRateLimit() {
194
	type RateLimits struct {
195
		Resources struct {
196
			Core struct {
197
				Limit     int `json:"limit"`
198
				Remaining int `json:"remaining"`
199
				Reset     int `json:"reset"`
200
			} `json:"core"`
201
			Search struct {
202
				Limit     int `json:"limit"`
203
				Remaining int `json:"remaining"`
204
				Reset     int `json:"reset"`
205
			} `json:"search"`
206
			GraphQL struct {
207
				Limit     int `json:"limit"`
208
				Remaining int `json:"remaining"`
209
				Reset     int `json:"reset"`
210
			} `json:"graphql"`
211
		} `json:"resources"`
212
		Rate struct {
213
			Limit     int `json:"limit"`
214
			Remaining int `json:"remaining"`
215
			Reset     int `json:"reset"`
216
		} `json:"rate"`
217
	}
218
	url := "https://api.github.com/rate_limit"
219
	resp, statusCode, err := httpcache.MakeHTTPRequest(url)
220
	if err != nil {
221
		log.Fatalf("Error during checking rate limit : %d %v#", statusCode, err)
222
	}
223
	jsonResponse, _, _ := httpcache.ReadResp(resp)
224
	rateLimits := RateLimits{}
225
	json.Unmarshal(jsonResponse, &rateLimits)
226
	fmt.Printf("Core: %d/%d (reset in %d minutes)\n", rateLimits.Resources.Core.Remaining, rateLimits.Resources.Core.Limit, timing.GetRelativeTime(rateLimits.Resources.Core.Reset))
227
	fmt.Printf("Search: %d/%d (reset in %d minutes)\n", rateLimits.Resources.Search.Remaining, rateLimits.Resources.Search.Limit, timing.GetRelativeTime(rateLimits.Resources.Search.Reset))
228
	fmt.Printf("GraphQL: %d/%d (reset in %d minutes)\n", rateLimits.Resources.GraphQL.Remaining, rateLimits.Resources.GraphQL.Limit, timing.GetRelativeTime(rateLimits.Resources.GraphQL.Reset))
229
	fmt.Printf("Rate: %d/%d (reset in %d minutes)\n", rateLimits.Rate.Remaining, rateLimits.Rate.Limit, timing.GetRelativeTime(rateLimits.Rate.Reset))
230
}
231
232
func writeCsv(csvFilePath string, headers []string, ghData [][]string) {
233
	file, err := os.Create(csvFilePath)
234
	if err != nil {
235
		log.Fatal("Cannot create file", err)
236
	}
237
	defer file.Close()
238
	writer := csv.NewWriter(file)
239
	defer writer.Flush()
240
	err = writer.Write(headers)
241
	if err != nil {
242
		log.Fatal("Cannot write to file", err)
243
	}
244
	for _, value := range ghData {
245
		err := writer.Write(value)
246
		if err != nil {
247
			log.Fatal("Cannot write to file", err)
248
		}
249
	}
250
}
251