Skip to content

Commit

Permalink
Merge pull request #266 from hossain-khan/renovate/org.jmailen.kotlin…
Browse files Browse the repository at this point in the history
…ter-4.x

Update plugin org.jmailen.kotlinter to v4
  • Loading branch information
hossain-khan authored Jan 6, 2024
2 parents 8cba166 + fe8df50 commit 202980c
Show file tree
Hide file tree
Showing 45 changed files with 1,313 additions and 1,133 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
// For build.gradle.kts (Kotlin DSL)
// https://kotlinlang.org/docs/releases.html#release-details
kotlin("jvm") version "1.9.22"
id("org.jmailen.kotlinter") version "3.16.0"
id("org.jmailen.kotlinter") version "4.1.1"
// https://kotlinlang.org/docs/ksp-quickstart.html#use-your-own-processor-in-a-project
// id("com.google.devtools.ksp") version "1.9.20-1.0.6" // Not needed yet.
application
Expand Down
40 changes: 21 additions & 19 deletions src/main/kotlin/PrStatsApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,31 @@ class PrStatsApplication : KoinComponent {
suspend fun generatePrStats(prNumber: Int) {
val (repoOwner, repoId, _, _) = appConfig.get()
Log.i("■ Building stats for PR#`$prNumber`.\n")
val authorReportBuildTime = measureTimeMillis {
val statsResult: PullRequestStatsRepo.StatsResult = try {
pullRequestStatsRepo.stats(
repoOwner = repoOwner,
repoId = repoId,
prNumber = prNumber,
)
} catch (e: Exception) {
val error = errorProcessor.getDetailedError(e)
PullRequestStatsRepo.StatsResult.Failure(error)
}
val authorReportBuildTime =
measureTimeMillis {
val statsResult: PullRequestStatsRepo.StatsResult =
try {
pullRequestStatsRepo.stats(
repoOwner = repoOwner,
repoId = repoId,
prNumber = prNumber,
)
} catch (e: Exception) {
val error = errorProcessor.getDetailedError(e)
PullRequestStatsRepo.StatsResult.Failure(error)
}

when (statsResult) {
is PullRequestStatsRepo.StatsResult.Success -> {
formatters.forEach {
println(it.formatSinglePrStats(statsResult.stats))
when (statsResult) {
is PullRequestStatsRepo.StatsResult.Success -> {
formatters.forEach {
println(it.formatSinglePrStats(statsResult.stats))
}
}
is PullRequestStatsRepo.StatsResult.Failure -> {
Log.w("⚠️ Failed to generate PR stats for PR#`$prNumber`. Error Message: ${statsResult.error.message}")
}
}
is PullRequestStatsRepo.StatsResult.Failure -> {
Log.w("⚠️ Failed to generate PR stats for PR#`$prNumber`. Error Message: ${statsResult.error.message}")
}
}
}

Log.d("\nⓘ Stats generation for PR#`$prNumber` took ${authorReportBuildTime.milliseconds}")

Expand Down
27 changes: 14 additions & 13 deletions src/main/kotlin/StatsGeneratorApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class StatsGeneratorApplication(
*/
private val formatters: List<StatsFormatter>,
) : KoinComponent {

/**
* Generates stats for user as PR author
* for all PRs created by each user defined in `[LOCAL_PROPERTIES_FILE]` config file.
Expand All @@ -48,13 +47,14 @@ class StatsGeneratorApplication(
appConfig.get().userIds.forEach { authorId ->
println(resources.string("status_building_author_pr_stats", authorId))

val authorReportBuildTime = measureTimeMillis {
val authorStats: AuthorStats = prAuthorStatsService.authorStats(prAuthorUserId = authorId)
allAuthorStats.add(authorStats)
formatters.forEach {
println(it.formatAuthorStats(authorStats))
val authorReportBuildTime =
measureTimeMillis {
val authorStats: AuthorStats = prAuthorStatsService.authorStats(prAuthorUserId = authorId)
allAuthorStats.add(authorStats)
formatters.forEach {
println(it.formatAuthorStats(authorStats))
}
}
}

Log.d(resources.string("stats_process_time_for_user", authorId, authorReportBuildTime.milliseconds))
Log.i("\n─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─\n")
Expand All @@ -78,13 +78,14 @@ class StatsGeneratorApplication(
printCurrentAppConfigs()
// For each user, generates stats for all the PRs reviewed by the user
appConfig.get().userIds.forEach { usedId ->
val reviewerReportBuildTime = measureTimeMillis {
println(resources.string("status_building_reviewer_pr_stats", usedId))
val prReviewerReviewStats = prReviewerStatsService.reviewerStats(prReviewerUserId = usedId)
formatters.forEach {
println(it.formatReviewerStats(prReviewerReviewStats))
val reviewerReportBuildTime =
measureTimeMillis {
println(resources.string("status_building_reviewer_pr_stats", usedId))
val prReviewerReviewStats = prReviewerStatsService.reviewerStats(prReviewerUserId = usedId)
formatters.forEach {
println(it.formatReviewerStats(prReviewerReviewStats))
}
}
}

Log.d(resources.string("stats_process_time_for_user", usedId, reviewerReportBuildTime.milliseconds))
Log.i("\n─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─\n")
Expand Down
32 changes: 16 additions & 16 deletions src/main/kotlin/dev/hossain/ascii/Art.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ object Art {
// Tea/Coffee art by Elissa Potier
return """
( ) ( ) )
) ( ) ( (
( ) ( ) )
_____________
<_____________> ___
| |/ _ \
| | | |
| |_| |
___| |\___/
/ \___________/ \
\_____________________/
Enjoy a cup of ☕️ while stats are being generated.
( ) ( ) )
) ( ) ( (
( ) ( ) )
_____________
<_____________> ___
| |/ _ \
| | | |
| |_| |
___| |\___/
/ \___________/ \
\_____________________/
Enjoy a cup of ☕️ while stats are being generated.
""".trimIndent()
""".trimIndent()
}

/**
* Meh! just 🤷
*/
const val shrug = "¯\\_(ツ)_/¯"
const val SHRUG = "¯\\_(ツ)_/¯"

/**
* Warns library user about the provided review time which can't be used literaly.
Expand Down Expand Up @@ -73,6 +73,6 @@ Enjoy a cup of ☕️ while stats are being generated.
! !
\__/
""".trimIndent()
""".trimIndent()
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/dev/hossain/githubstats/BuildConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object BuildConfig {
* @see Log.WARNING
* @see Log.NONE
*/
var LOG_LEVEL = Log.DEBUG
var logLevel = Log.DEBUG

/**
* Shows HTTP requests and response on the console.
Expand Down
171 changes: 90 additions & 81 deletions src/main/kotlin/dev/hossain/githubstats/PrAuthorStatsService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class PrAuthorStatsService constructor(
private val appConfig: AppConfig,
private val errorProcessor: ErrorProcessor,
) {

/**
* Generates stats for reviews given by different PR reviewers for specified PR [prAuthorUserId].
*
Expand All @@ -38,59 +37,63 @@ class PrAuthorStatsService constructor(
* Jim -> 13 PRs reviewed for Bob; Average Review Time: 14 hours 21 min
* ```
*/
suspend fun authorStats(
prAuthorUserId: String,
): AuthorStats {
suspend fun authorStats(prAuthorUserId: String): AuthorStats {
val (repoOwner, repoId, _, dateLimitAfter, dateLimitBefore) = appConfig.get()

// First get all the recent PRs made by author
val allMergedPrsByAuthor: List<Issue> = issueSearchPager.searchIssues(
searchQuery = SearchParams(
repoOwner = repoOwner,
repoId = repoId,
author = prAuthorUserId,
dateAfter = dateLimitAfter,
dateBefore = dateLimitBefore,
).toQuery(),
).filter {
// Makes sure it is a PR, not an issue
it.pull_request != null
}
val allMergedPrsByAuthor: List<Issue> =
issueSearchPager.searchIssues(
searchQuery =
SearchParams(
repoOwner = repoOwner,
repoId = repoId,
author = prAuthorUserId,
dateAfter = dateLimitAfter,
dateBefore = dateLimitBefore,
).toQuery(),
).filter {
// Makes sure it is a PR, not an issue
it.pull_request != null
}

// Provides periodic progress updates based on config
val progress = PrAnalysisProgress(allMergedPrsByAuthor).also { it.start() }

// For each PR by author, get the review stats on the PR
val mergedPrsStatsList: List<PrStats> = allMergedPrsByAuthor
.mapIndexed { index, pr ->
progress.publish(index)

// ⏰ Slight delay to avoid GitHub API rate-limit
delay(BuildConfig.API_REQUEST_DELAY_MS)

try {
pullRequestStatsRepo.stats(
repoOwner = repoOwner,
repoId = repoId,
prNumber = pr.number,
)
} catch (e: Exception) {
val error = errorProcessor.getDetailedError(e)
Log.w("Error getting PR#${pr.number}. Got: ${error.message}")
StatsResult.Failure(error)
val mergedPrsStatsList: List<PrStats> =
allMergedPrsByAuthor
.mapIndexed { index, pr ->
progress.publish(index)

// ⏰ Slight delay to avoid GitHub API rate-limit
delay(BuildConfig.API_REQUEST_DELAY_MS)

try {
pullRequestStatsRepo.stats(
repoOwner = repoOwner,
repoId = repoId,
prNumber = pr.number,
)
} catch (e: Exception) {
val error = errorProcessor.getDetailedError(e)
Log.w("Error getting PR#${pr.number}. Got: ${error.message}")
StatsResult.Failure(error)
}
}
.filterIsInstance<StatsResult.Success>()
.map {
it.stats
}
}
.filterIsInstance<StatsResult.Success>()
.map {
it.stats
}

progress.end()

val authorPrStats = aggregatePrAuthorsPrStats(prAuthorUserId, allMergedPrsByAuthor, mergedPrsStatsList)
Log.i(
"ℹ️ The author '$prAuthorUserId' has created ${authorPrStats.totalPrsCreated} PRs that successfully got merged." +
"\nTotal Comments Received - Code Review: ${authorPrStats.totalCodeReviewComments}, PR Comment: ${authorPrStats.totalIssueComments}, Review+Re-review: ${authorPrStats.totalPrSubmissionComments}",
"\nTotal Comments Received - " +
"Code Review: ${authorPrStats.totalCodeReviewComments}, " +
"PR Comment: ${authorPrStats.totalIssueComments}, " +
"Review+Re-review: ${authorPrStats.totalPrSubmissionComments}",
)

val authorReviewStats: List<AuthorReviewStats> =
Expand All @@ -110,15 +113,16 @@ class PrAuthorStatsService constructor(
mergedPrsStatsList.filter { it.prApprovalTime.isNotEmpty() }
.forEach { stats: PrStats ->
stats.prApprovalTime.forEach { (userId, time) ->
val reviewStats = ReviewStats(
reviewerUserId = userId,
pullRequest = stats.pullRequest,
reviewCompletion = time,
initialResponseTime = stats.initialResponseTime[userId] ?: time,
prComments = stats.comments[userId] ?: noComments(userId),
prReadyOn = stats.prReadyOn,
prMergedOn = stats.prMergedOn,
)
val reviewStats =
ReviewStats(
reviewerUserId = userId,
pullRequest = stats.pullRequest,
reviewCompletion = time,
initialResponseTime = stats.initialResponseTime[userId] ?: time,
prComments = stats.comments[userId] ?: noComments(userId),
prReadyOn = stats.prReadyOn,
prMergedOn = stats.prMergedOn,
)
if (userReviews.containsKey(userId)) {
userReviews[userId] = userReviews[userId]!!.plus(reviewStats)
} else {
Expand All @@ -127,24 +131,26 @@ class PrAuthorStatsService constructor(
}
}

val authorReviewStats: List<AuthorReviewStats> = userReviews.map { (reviewerUserId, reviewStats) ->
val totalReviews: Int = reviewStats.size
val averageReviewTime: Duration = reviewStats
.map { it.reviewCompletion }
.fold(Duration.ZERO, Duration::plus)
.div(totalReviews)
val totalReviewComments = reviewStats.sumOf { it.prComments.allComments }

AuthorReviewStats(
repoId = repoId,
prAuthorId = prAuthorUsedId,
reviewerId = reviewerUserId,
average = averageReviewTime,
totalReviews = totalReviews,
totalComments = totalReviewComments,
stats = reviewStats,
)
}.sortedByDescending { it.totalReviews }
val authorReviewStats: List<AuthorReviewStats> =
userReviews.map { (reviewerUserId, reviewStats) ->
val totalReviews: Int = reviewStats.size
val averageReviewTime: Duration =
reviewStats
.map { it.reviewCompletion }
.fold(Duration.ZERO, Duration::plus)
.div(totalReviews)
val totalReviewComments = reviewStats.sumOf { it.prComments.allComments }

AuthorReviewStats(
repoId = repoId,
prAuthorId = prAuthorUsedId,
reviewerId = reviewerUserId,
average = averageReviewTime,
totalReviews = totalReviews,
totalComments = totalReviewComments,
stats = reviewStats,
)
}.sortedByDescending { it.totalReviews }

return authorReviewStats
}
Expand All @@ -164,21 +170,24 @@ class PrAuthorStatsService constructor(
): AuthorPrStats {
// Builds author's stats for all PRs made by the author
val totalPrsCreated = allMergedPrsByAuthor.size
val totalIssueComments = mergedPrsStatsList.sumOf {
it.comments.entries
.filter { prCommentEntry -> prCommentEntry.key != prAuthorUserId }
.sumOf { commentEntry -> commentEntry.value.issueComment }
}
val totalPrSubmissionComments = mergedPrsStatsList.sumOf {
it.comments.entries
.filter { prCommentEntry -> prCommentEntry.key != prAuthorUserId }
.sumOf { commentEntry -> commentEntry.value.prReviewSubmissionComment }
}
val totalCodeReviewComments = mergedPrsStatsList.sumOf {
it.comments.entries
.filter { prCommentEntry -> prCommentEntry.key != prAuthorUserId }
.sumOf { commentEntry -> commentEntry.value.codeReviewComment }
}
val totalIssueComments =
mergedPrsStatsList.sumOf {
it.comments.entries
.filter { prCommentEntry -> prCommentEntry.key != prAuthorUserId }
.sumOf { commentEntry -> commentEntry.value.issueComment }
}
val totalPrSubmissionComments =
mergedPrsStatsList.sumOf {
it.comments.entries
.filter { prCommentEntry -> prCommentEntry.key != prAuthorUserId }
.sumOf { commentEntry -> commentEntry.value.prReviewSubmissionComment }
}
val totalCodeReviewComments =
mergedPrsStatsList.sumOf {
it.comments.entries
.filter { prCommentEntry -> prCommentEntry.key != prAuthorUserId }
.sumOf { commentEntry -> commentEntry.value.codeReviewComment }
}

return AuthorPrStats(
authorUserId = prAuthorUserId,
Expand Down
Loading

0 comments on commit 202980c

Please sign in to comment.