Skip to content

Commit fdbe4a6

Browse files
feat: add progress bar, refactor messages (APP-102) (#70)
* feat: add progress bar to commitcrawler * wip: refactor messages * wip: refactor messages * fix: tests
1 parent 0298f93 commit fdbe4a6

16 files changed

+162
-89
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ app.iws
2121
/ultimate/ideaSDK
2222
/ultimate/out
2323
/ultimate/tmp
24+
tmp_repo

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ buildConfig {
4949

5050
// Logging.
5151
buildConfigField 'String', 'SENTRY_DSN', 'https://0263d6473bd24a9ba40e25aa5fb0a242:[email protected]/233260'
52-
buildConfigField 'boolean', 'PRINT_STACK_TRACE', 'true'
52+
buildConfigField 'boolean', 'PRINT_STACK_TRACE', 'false'
5353

5454
// Models storage path.
5555
buildConfigField 'String', 'LIBRARY_MODELS_URL', 'https://storage.googleapis.com/sourcerer-app/library-models/v1/'

src/main/kotlin/app/Logger.kt

+43
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,49 @@ object Logger {
9797
return a.getValue(BuildConfig.LOG_LEVEL)
9898
}
9999

100+
/**
101+
* Utils.
102+
*/
103+
private fun Double.format(digits: Int, digitsFloating: Int) =
104+
java.lang.String.format("%${digits}.${digitsFloating}f", this)
105+
106+
private fun generateIndent(num: Int): String {
107+
return 0.rangeTo(num).fold("") { ind, _ -> ind + " " }
108+
}
109+
110+
/**
111+
* CLI messages and pretty printing.
112+
*/
113+
fun print(message: Any, indentLine: Boolean = false) {
114+
print(message.toString(), indentLine)
115+
}
116+
117+
fun print(message: String, indentLine: Boolean = false) {
118+
if (indentLine) {
119+
println()
120+
}
121+
println(message)
122+
}
123+
124+
fun printCommit(commitMessage: String, commitHash: String,
125+
percents: Double) {
126+
val percentsStr = percents.format(6, 2)
127+
val hash = commitHash.substring(0, 7)
128+
val messageTrim = if (commitMessage.length > 59) {
129+
commitMessage.substring(0, 56).plus("...")
130+
} else commitMessage
131+
println(" [$percentsStr%] * $hash $messageTrim")
132+
}
133+
134+
private val commitDetailIndent = generateIndent(10) + "|" +
135+
generateIndent(8)
136+
fun printCommitDetail(message: String) {
137+
val messageTrim = if (message.length > 59) {
138+
message.substring(0, 56).plus("...")
139+
} else message
140+
println(commitDetailIndent + messageTrim)
141+
}
142+
100143
/**
101144
* Log error message with exception info.
102145
* Don't log private information with this method.

src/main/kotlin/app/Main.kt

+9-8
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ class Main(argv: Array<String>) {
8181
localRepo.hashAllContributors = commandAdd.hashAll
8282
configurator.addLocalRepoPersistent(localRepo)
8383
configurator.saveToFile()
84-
println("Added git repository at $path.")
85-
84+
Logger.print("Added git repository at $path.")
8685
Logger.info(Logger.Events.CONFIG_CHANGED) { "Config changed" }
8786
} else {
8887
Logger.warn { "No valid git repository found at specified path" }
@@ -119,11 +118,11 @@ class Main(argv: Array<String>) {
119118
if (path != null) {
120119
configurator.removeLocalRepoPersistent(LocalRepo(path))
121120
configurator.saveToFile()
122-
println("Repository removed from tracking list.")
121+
Logger.print("Repository removed from tracking list.")
123122

124123
Logger.info(Logger.Events.CONFIG_CHANGED) { "Config changed" }
125124
} else {
126-
println("Repository not found in tracking list.")
125+
Logger.print("Repository not found in tracking list.")
127126
}
128127
}
129128

@@ -138,10 +137,12 @@ class Main(argv: Array<String>) {
138137
}
139138

140139
private fun showHelp(jc: JCommander) {
141-
println("Sourcerer hashes your git repositories into intelligent "
142-
+ "engineering profiles. If you don't have an account, "
143-
+ "please, proceed to http://sourcerer.io/register. More info at "
144-
+ "http://sourcerer.io.")
140+
Logger.print("Sourcerer hashes your git repositories into intelligent "
141+
+ "engineering profiles.")
142+
Logger.print("If you don't have an account, please, proceed to " +
143+
"https://sourcerer.io/join")
144+
Logger.print("More info at https://sourcerer.io and " +
145+
"https://github.com/sourcerer-io")
145146
jc.usage() // Will show detailed info about usage based on annotations.
146147
}
147148
}

src/main/kotlin/app/hashers/CommitCrawler.kt

+44-36
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,52 @@ import org.eclipse.jgit.revwalk.RevWalk
2222
import org.eclipse.jgit.util.io.DisabledOutputStream
2323

2424
object CommitCrawler {
25-
fun getObservable(git: Git, repo: Repo): Observable<Commit> = Observable
26-
.create<Commit> { subscriber ->
27-
try {
28-
val revWalk = RevWalk(git.repository)
29-
val commitId = git.repository.resolve(RepoHelper.MASTER_BRANCH)
30-
revWalk.markStart(revWalk.parseCommit(commitId))
31-
for (revCommit in revWalk) {
32-
subscriber.onNext(Commit(revCommit))
25+
fun getObservable(git: Git, repo: Repo,
26+
numCommits: Int = 0): Observable<Commit> {
27+
var curNumCommits = 0
28+
return Observable
29+
.create<Commit> { subscriber ->
30+
try {
31+
val revWalk = RevWalk(git.repository)
32+
val commitId = git.repository.resolve(RepoHelper.MASTER_BRANCH)
33+
revWalk.markStart(revWalk.parseCommit(commitId))
34+
for (revCommit in revWalk) {
35+
subscriber.onNext(Commit(revCommit))
36+
}
37+
// Commits are combined in pairs, an empty commit concatenated
38+
// to calculate the diff of the initial commit.
39+
subscriber.onNext(Commit())
40+
} catch (e: Exception) {
41+
Logger.error(e, "Commit producing error")
42+
subscriber.onError(e)
3343
}
34-
// Commits are combined in pairs, an empty commit concatenated
35-
// to calculate the diff of the initial commit.
36-
subscriber.onNext(Commit())
37-
} catch (e: Exception) {
38-
Logger.error(e, "Commit producing error")
39-
subscriber.onError(e)
40-
}
41-
subscriber.onComplete()
42-
} // TODO(anatoly): Rewrite diff calculation in non-weird way.
43-
.pairWithNext() // Pair commits to get diff.
44-
.map { (new, old) ->
45-
// Mapping and stats extraction.
46-
Logger.debug {
47-
"Commit: ${new.raw?.name ?: ""}: ${new.raw?.shortMessage}"
48-
}
49-
new.diffs = getDiffFiles(git, new, old)
50-
Logger.debug { "Diff: ${new.diffs.size} entries" }
51-
// Count lines on all non-binary files. This is additional
52-
// statistics to CommitStats because not all file extensions
53-
// may be supported.
54-
new.numLinesAdded = new.diffs.fold(0) { total, file ->
55-
total + file.getAllAdded().size
56-
}
57-
new.numLinesDeleted = new.diffs.fold(0) { total, file ->
58-
total + file.getAllDeleted().size
44+
subscriber.onComplete()
45+
} // TODO(anatoly): Rewrite diff calculation in non-weird way.
46+
.pairWithNext() // Pair commits to get diff.
47+
.map { (new, old) ->
48+
curNumCommits++
49+
// Mapping and stats extraction.
50+
val message = new.raw?.shortMessage ?: ""
51+
val hash = new.raw?.name ?: ""
52+
val perc = if (numCommits != 0) {
53+
(curNumCommits.toDouble() / numCommits) * 100
54+
} else 0.0
55+
Logger.printCommit(message, hash, perc)
56+
new.diffs = getDiffFiles(git, new, old)
57+
Logger.debug { "Diff: ${new.diffs.size} entries" }
58+
// Count lines on all non-binary files. This is additional
59+
// statistics to CommitStats because not all file extensions
60+
// may be supported.
61+
new.numLinesAdded = new.diffs.fold(0) { total, file ->
62+
total + file.getAllAdded().size
63+
}
64+
new.numLinesDeleted = new.diffs.fold(0) { total, file ->
65+
total + file.getAllDeleted().size
66+
}
67+
new.repo = repo
68+
new
5969
}
60-
new.repo = repo
61-
new
62-
}
70+
}
6371

6472
private fun getDiffFiles(git: Git,
6573
commitNew: Commit,

src/main/kotlin/app/hashers/CommitHasher.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ class CommitHasher(private val serverRepo: Repo = Repo(),
4848

4949
// Mapping and stats extraction.
5050
commit.stats = Extractor().extract(commit.diffs)
51-
Logger.info { "Stats: ${commit.stats.size} entries" }
51+
if (commit.stats.isNotEmpty()) {
52+
Logger.printCommitDetail("${commit.stats.size} " +
53+
"technology stats found")
54+
}
5255
Logger.debug { commit.stats.toString() }
5356

5457
commit

src/main/kotlin/app/hashers/RepoHasher.kt

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package app.hashers
55

6+
import app.BuildConfig
67
import app.Logger
78
import app.api.Api
89
import app.config.Configurator
@@ -31,7 +32,6 @@ class RepoHasher(private val localRepo: LocalRepo, private val api: Api,
3132
}
3233

3334
fun update() {
34-
println("Hashing $localRepo...")
3535
Logger.info { "Hashing of repo started" }
3636
val git = loadGit(localRepo.path)
3737
try {
@@ -60,8 +60,8 @@ class RepoHasher(private val localRepo: LocalRepo, private val api: Api,
6060
}
6161

6262
// Hash by all plugins.
63-
val observable = CommitCrawler.getObservable(git, serverRepo)
64-
.publish()
63+
val observable = CommitCrawler.getObservable(git, serverRepo,
64+
rehashes.size).publish()
6565
CommitHasher(serverRepo, api, rehashes, filteredEmails)
6666
.updateFromObservable(observable, onError)
6767
FactHasher(serverRepo, api, rehashes, filteredEmails)
@@ -71,19 +71,20 @@ class RepoHasher(private val localRepo: LocalRepo, private val api: Api,
7171
observable.connect()
7272

7373
// TODO(anatoly): CodeLongevity hash from observable.
74+
Logger.print("Code longevity calculation. May take a while...")
7475
try {
7576
CodeLongevity(serverRepo, filteredEmails, git, onError)
7677
.updateStats(api)
7778
}
7879
catch (e: Throwable) {
7980
onError(e)
8081
}
82+
Logger.print("Finished.")
8183

8284
if (errors.isNotEmpty()) {
8385
throw HashingException(errors)
8486
}
8587

86-
println("Hashing $localRepo completed.")
8788
Logger.info(Logger.Events.HASHING_REPO_SUCCESS)
8889
{ "Hashing repo completed" }
8990
}

src/main/kotlin/app/model/LocalRepo.kt

+4
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ data class LocalRepo(var path: String = "") {
1818
email = config.getString("user", null, "email") ?: "")
1919
remoteOrigin = config.getString("remote", "origin", "url") ?: ""
2020
}
21+
22+
override fun toString(): String {
23+
return path
24+
}
2125
}

src/main/kotlin/app/ui/AddRepoState.kt

+7-6
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,29 @@ class AddRepoState constructor(private val context: Context,
2121
if (configurator.getLocalRepos().isNotEmpty()) return
2222

2323
while (true) {
24-
println("Type a path to repository, or hit Enter to continue.")
24+
Logger.print("Type a path to repository, or hit Enter to continue.")
2525
val pathString = readLine() ?: ""
2626

2727
if (pathString.isEmpty()) {
2828
if (configurator.getLocalRepos().isEmpty()) {
29-
println("Add at least one valid repository.")
29+
Logger.print("Add at least one valid repository.")
3030
} else {
3131
break // User finished to add repos.
3232
}
3333
} else {
3434
if (RepoHelper.isValidRepo(pathString)) {
35-
println("Added git repository at $pathString.")
35+
Logger.print("Added git repository at $pathString.")
3636
val localRepo = LocalRepo(pathString)
3737
localRepo.hashAllContributors = UiHelper.confirm("Do you "
3838
+ "want to hash commits of all contributors?",
3939
defaultIsYes = true)
4040
configurator.addLocalRepoPersistent(localRepo)
4141
configurator.saveToFile()
4242
} else {
43-
println("Directory should contain valid git repository.")
44-
println("Make sure that master branch with at least one " +
45-
"commit exists.")
43+
Logger.print("Directory should contain a valid git " +
44+
"repository.")
45+
Logger.print("Make sure that master branch with at least " +
46+
"one commit exists.")
4647
}
4748
}
4849
}

src/main/kotlin/app/ui/AuthState.kt

+11-11
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ class AuthState constructor(private val context: Context,
4646
}
4747

4848
fun getUsername() {
49-
println("Enter username:")
49+
Logger.print("Enter username:")
5050
username = readLine() ?: ""
5151
configurator.setUsernameCurrent(username)
5252
}
5353

5454
fun getPassword() {
55-
println("Enter password:")
55+
Logger.print("Enter password:")
5656
password = PasswordHelper.readPassword()
5757
configurator.setPasswordCurrent(password)
5858
}
@@ -71,11 +71,11 @@ class AuthState constructor(private val context: Context,
7171

7272
fun tryAuth(): Boolean {
7373
try {
74-
println("Authenticating...")
74+
Logger.print("Signing in...")
7575
val (_, error) = api.authorize()
7676
if (error.isWithServerCode(Api.OUT_OF_DATE)) {
77-
println("App is out of date. Please get new version at " +
78-
"https://sourcerer.io")
77+
Logger.print("App is out of date. Please get new version at " +
78+
"https://sourcerer.io")
7979
retry = false
8080
return false
8181
}
@@ -85,8 +85,8 @@ class AuthState constructor(private val context: Context,
8585
val user = api.getUser().getOrThrow()
8686
configurator.setUser(user)
8787

88-
println("You are successfully authenticated. Your profile page is "
89-
+ BuildConfig.PROFILE_URL + configurator.getUsername())
88+
Logger.print("Signed in successfully. Your profile page is " +
89+
BuildConfig.PROFILE_URL + configurator.getUsername())
9090

9191
saveCredentialsIfChanged()
9292
Logger.username = configurator.getUsername()
@@ -95,14 +95,14 @@ class AuthState constructor(private val context: Context,
9595
return true
9696
} catch (e: ApiError) {
9797
if (e.isAuthError) {
98-
if (e.httpBodyMessage.isNotBlank()) {
99-
println(e.httpBodyMessage)
98+
if(e.httpBodyMessage.isNotBlank()) {
99+
Logger.print(e.httpBodyMessage)
100100
} else {
101-
println("Authentication error. Try again.")
101+
Logger.print("Authentication error. Try again.")
102102
}
103103
} else {
104+
Logger.print("Connection problems. Try again later.")
104105
Logger.error(e)
105-
println("Connection problems. Try again later.")
106106
retry = false
107107
}
108108
}

src/main/kotlin/app/ui/CloseState.kt

+7-5
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33

44
package app.ui
55

6+
import app.Logger
7+
68
/**
79
* On application close console UI state.
810
*/
911
class CloseState : ConsoleState {
1012
override fun doAction() {
11-
println("You could use console commands to control repositories. To "
12-
+ "setup again run application with flag --setup. For more "
13-
+ "info run application with flag --help.")
13+
Logger.print("You could use console commands to control repositories.",
14+
indentLine = true)
15+
Logger.print("For more info run application with flag --help.")
1416
// TODO(anatoly): Check for problems for display support message.
15-
println("Feel free to contact us on any problem by "
16-
17+
Logger.print("Feel free to contact us on any problem by " +
18+
1719
}
1820

1921
override fun next() {

0 commit comments

Comments
 (0)