diff --git a/server/application-server/openapi.yaml b/server/application-server/openapi.yaml
index d01f4540..6fbf65bc 100644
--- a/server/application-server/openapi.yaml
+++ b/server/application-server/openapi.yaml
@@ -219,6 +219,7 @@ components:
- htmlUrl
- id
- isDismissed
+ - score
- state
type: object
properties:
@@ -243,6 +244,9 @@ components:
$ref: "#/components/schemas/PullRequestBaseInfo"
htmlUrl:
type: string
+ score:
+ type: integer
+ format: int32
submittedAt:
type: string
format: date-time
diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java
index 324d1e13..7dc6f946 100644
--- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java
+++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java
@@ -33,4 +33,15 @@ SELECT MIN(p.createdAt)
List findAssignedByLoginAndStates(
@Param("assigneeLogin") String assigneeLogin,
@Param("states") Set states);
+
+ @Query("""
+ SELECT p
+ FROM PullRequest p
+ LEFT JOIN FETCH p.labels
+ JOIN FETCH p.author
+ LEFT JOIN FETCH p.assignees
+ LEFT JOIN FETCH p.repository
+ WHERE p.repository = :repository AND p.number = :number
+ """)
+ Optional findByRepositoryAndNumber(@Param("repository") de.tum.in.www1.hephaestus.gitprovider.repository.Repository repository, @Param("number") int number);
}
\ No newline at end of file
diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/PullRequestReviewInfoDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/PullRequestReviewInfoDTO.java
index fd5049fa..1f494042 100644
--- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/PullRequestReviewInfoDTO.java
+++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/PullRequestReviewInfoDTO.java
@@ -1,11 +1,9 @@
package de.tum.in.www1.hephaestus.gitprovider.pullrequestreview;
import java.time.OffsetDateTime;
-
import org.springframework.lang.NonNull;
import com.fasterxml.jackson.annotation.JsonInclude;
-import de.tum.in.www1.hephaestus.gitprovider.issuecomment.IssueComment;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestBaseInfoDTO;
import de.tum.in.www1.hephaestus.gitprovider.user.UserInfoDTO;
@@ -18,31 +16,7 @@ public record PullRequestReviewInfoDTO(
UserInfoDTO author,
PullRequestBaseInfoDTO pullRequest,
@NonNull String htmlUrl,
+ @NonNull int score,
OffsetDateTime submittedAt) {
// We do not have createdAt and updatedAt for reviews
-
- public static PullRequestReviewInfoDTO fromPullRequestReview(PullRequestReview pullRequestReview) {
- return new PullRequestReviewInfoDTO(
- pullRequestReview.getId(),
- pullRequestReview.isDismissed(),
- pullRequestReview.getState(),
- pullRequestReview.getComments().size(),
- UserInfoDTO.fromUser(pullRequestReview.getAuthor()),
- PullRequestBaseInfoDTO.fromPullRequest(pullRequestReview.getPullRequest()),
- pullRequestReview.getHtmlUrl(),
- pullRequestReview.getSubmittedAt());
- }
-
- public static PullRequestReviewInfoDTO fromIssueComment(IssueComment issueComment) {
- return new PullRequestReviewInfoDTO(
- issueComment.getId(),
- false,
- PullRequestReview.State.COMMENTED,
- 0,
- UserInfoDTO.fromUser(issueComment.getAuthor()),
- PullRequestBaseInfoDTO.fromIssue(issueComment.getIssue()),
- issueComment.getHtmlUrl(),
- issueComment.getCreatedAt()
- );
- }
}
diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/PullRequestReviewInfoDTOConverter.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/PullRequestReviewInfoDTOConverter.java
new file mode 100644
index 00000000..c15ffc4c
--- /dev/null
+++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/PullRequestReviewInfoDTOConverter.java
@@ -0,0 +1,47 @@
+package de.tum.in.www1.hephaestus.gitprovider.pullrequestreview;
+
+import de.tum.in.www1.hephaestus.gitprovider.issuecomment.IssueComment;
+import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestBaseInfoDTO;
+import de.tum.in.www1.hephaestus.gitprovider.user.UserInfoDTO;
+import de.tum.in.www1.hephaestus.leaderboard.ScoringService;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+
+@Component
+public class PullRequestReviewInfoDTOConverter implements Converter {
+
+ @Autowired
+ private ScoringService scoringService;
+
+ @Override
+ public PullRequestReviewInfoDTO convert(@NonNull PullRequestReview source) {
+ return new PullRequestReviewInfoDTO(
+ source.getId(),
+ source.isDismissed(),
+ source.getState(),
+ source.getComments().size(),
+ UserInfoDTO.fromUser(source.getAuthor()),
+ PullRequestBaseInfoDTO.fromPullRequest(source.getPullRequest()),
+ source.getHtmlUrl(),
+ (int) scoringService.calculateReviewScore(List.of(source)),
+ source.getSubmittedAt()
+ );
+ }
+
+ public PullRequestReviewInfoDTO convert(@NonNull IssueComment source) {
+ return new PullRequestReviewInfoDTO(
+ source.getId(),
+ false,
+ PullRequestReview.State.COMMENTED,
+ 0,
+ UserInfoDTO.fromUser(source.getAuthor()),
+ PullRequestBaseInfoDTO.fromIssue(source.getIssue()),
+ source.getHtmlUrl(),
+ (int) scoringService.calculateReviewScore(source),
+ source.getCreatedAt()
+ );
+ }
+}
diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/UserService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/UserService.java
index 75159b16..504256db 100644
--- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/UserService.java
+++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/UserService.java
@@ -10,14 +10,15 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.tum.in.www1.hephaestus.gitprovider.issue.Issue;
-import de.tum.in.www1.hephaestus.gitprovider.issuecomment.IssueComment;
import de.tum.in.www1.hephaestus.gitprovider.issuecomment.IssueCommentRepository;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestInfoDTO;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository;
import de.tum.in.www1.hephaestus.gitprovider.pullrequestreview.PullRequestReviewInfoDTO;
+import de.tum.in.www1.hephaestus.gitprovider.pullrequestreview.PullRequestReviewInfoDTOConverter;
import de.tum.in.www1.hephaestus.gitprovider.pullrequestreview.PullRequestReviewRepository;
import de.tum.in.www1.hephaestus.gitprovider.repository.RepositoryInfoDTO;
import de.tum.in.www1.hephaestus.gitprovider.repository.RepositoryRepository;
@@ -27,24 +28,18 @@
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
- private final UserRepository userRepository;
- private final RepositoryRepository repositoryRepository;
- private final PullRequestRepository pullRequestRepository;
- private final PullRequestReviewRepository pullRequestReviewRepository;
- private final IssueCommentRepository issueCommentRepository;
-
- public UserService(
- UserRepository userRepository,
- RepositoryRepository repositoryRepository,
- PullRequestRepository pullRequestRepository,
- PullRequestReviewRepository pullRequestReviewRepository,
- IssueCommentRepository issueCommentRepository) {
- this.userRepository = userRepository;
- this.repositoryRepository = repositoryRepository;
- this.pullRequestRepository = pullRequestRepository;
- this.pullRequestReviewRepository = pullRequestReviewRepository;
- this.issueCommentRepository = issueCommentRepository;
- }
+ @Autowired
+ private UserRepository userRepository;
+ @Autowired
+ private RepositoryRepository repositoryRepository;
+ @Autowired
+ private PullRequestRepository pullRequestRepository;
+ @Autowired
+ private PullRequestReviewRepository pullRequestReviewRepository;
+ @Autowired
+ private IssueCommentRepository issueCommentRepository;
+ @Autowired
+ private PullRequestReviewInfoDTOConverter pullRequestReviewInfoDTOConverter;
@Transactional
public Optional getUserProfile(String login) {
@@ -72,13 +67,13 @@ public Optional getUserProfile(String login) {
List reviewActivity = pullRequestReviewRepository
.findAllByAuthorLoginSince(login, OffsetDateTime.now().minusDays(7))
.stream()
- .map(PullRequestReviewInfoDTO::fromPullRequestReview)
+ .map(pullRequestReviewInfoDTOConverter::convert)
.collect(Collectors.toCollection(ArrayList::new));
reviewActivity.addAll(
issueCommentRepository
.findAllByAuthorLoginSince(login, OffsetDateTime.now().minusDays(7), true)
.stream()
- .map(PullRequestReviewInfoDTO::fromIssueComment)
+ .map(pullRequestReviewInfoDTOConverter::convert)
.toList()
);
reviewActivity.sort(Comparator.comparing(PullRequestReviewInfoDTO::submittedAt).reversed());
diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/leaderboard/LeaderboardService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/leaderboard/LeaderboardService.java
index 2ada550b..448fcac2 100644
--- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/leaderboard/LeaderboardService.java
+++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/leaderboard/LeaderboardService.java
@@ -18,6 +18,7 @@
import java.util.stream.IntStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -26,20 +27,16 @@ public class LeaderboardService {
private static final Logger logger = LoggerFactory.getLogger(LeaderboardService.class);
- private final PullRequestReviewRepository pullRequestReviewRepository;
- private final IssueCommentRepository issueCommentRepository;
+ @Autowired
+ private PullRequestReviewRepository pullRequestReviewRepository;
+ @Autowired
+ private IssueCommentRepository issueCommentRepository;
+ @Autowired
+ private ScoringService scoringService;
@Value("${monitoring.timeframe}")
private int timeframe;
- public LeaderboardService(
- PullRequestReviewRepository pullRequestReviewRepository,
- IssueCommentRepository issueCommentRepository
- ) {
- this.pullRequestReviewRepository = pullRequestReviewRepository;
- this.issueCommentRepository = issueCommentRepository;
- }
-
@Transactional
public List createLeaderboard(
OffsetDateTime after,
@@ -166,90 +163,8 @@ private int calculateTotalScore(List reviews, int numberOfIss
double totalScore = reviewsByPullRequestId
.values()
.stream()
- .map(pullRequestReviews -> {
- // All reviews are for the same pull request
- int complexityScore = calculateComplexityScore(pullRequestReviews.get(0).getPullRequest());
-
- double approvalWeight = 2.0;
- double approvalScore = pullRequestReviews
- .stream()
- .filter(review -> review.getState() == PullRequestReview.State.APPROVED)
- .filter(review -> review.getAuthor().getId() != review.getPullRequest().getAuthor().getId())
- .map(review -> approvalWeight * calculateCodeReviewBonus(review.getComments().size(), complexityScore))
- .reduce(0.0, Double::sum);
-
- double changesRequestedWeight = 2.5;
- double changesRequestedScore = pullRequestReviews
- .stream()
- .filter(review -> review.getState() == PullRequestReview.State.CHANGES_REQUESTED)
- .filter(review -> review.getAuthor().getId() != review.getPullRequest().getAuthor().getId())
- .map(review -> changesRequestedWeight * calculateCodeReviewBonus(review.getComments().size(), complexityScore))
- .reduce(0.0, Double::sum);
-
- double commentWeight = 1.5;
- double commentScore = pullRequestReviews
- .stream()
- .filter(review -> review.getState() == PullRequestReview.State.COMMENTED || review.getState() == PullRequestReview.State.UNKNOWN)
- .filter(review -> review.getAuthor().getId() != review.getPullRequest().getAuthor().getId())
- .map(review -> commentWeight * calculateCodeReviewBonus(review.getComments().size(), complexityScore))
- .reduce(0.0, Double::sum);
-
- double issueCommentScore = commentWeight * numberOfIssueComments;
-
- double interactionScore = approvalScore + changesRequestedScore + commentScore + issueCommentScore;
- return 10 * interactionScore * complexityScore / (interactionScore + complexityScore);
- })
+ .map(pullRequestReviews -> scoringService.calculateReviewScore(pullRequestReviews, numberOfIssueComments))
.reduce(0.0, Double::sum);
return (int) Math.ceil(totalScore);
}
-
- /**
- * Calculates the complexity score for a given pull request.
- * Possible values: 1, 3, 7, 17, 33.
- * Taken from the original leaderboard implementation script.
- *
- * @param pullRequest
- * @return score
- */
- private int calculateComplexityScore(PullRequest pullRequest) {
- Double complexityScore =
- ((pullRequest.getChangedFiles() * 3) +
- (pullRequest.getCommits() * 0.5) +
- pullRequest.getAdditions() +
- pullRequest.getDeletions()) /
- 10;
- if (complexityScore < 10) {
- return 1; // Simple
- } else if (complexityScore < 50) {
- return 3; // Medium
- } else if (complexityScore < 100) {
- return 7; // Large
- } else if (complexityScore < 500) {
- return 17; // Huge
- }
- return 33; // Overly complex
- }
-
- /**
- * Calculates the code review bonus for a given number of code comments and complexity score.
- * The bonus is a value between 0 and 2.
- * Taken from the original leaderboard implementation script.
- *
- * @param codeComments
- * @param complexityScore
- * @return bonus
- */
- private double calculateCodeReviewBonus(int codeComments, int complexityScore) {
- double maxBonus = 2;
-
- double codeReviewBonus = 1;
- if (codeComments < complexityScore) {
- // Function goes from 0 at codeComments = 0 to 1 at codeComments = complexityScore
- codeReviewBonus += 2 * Math.sqrt(complexityScore) * Math.sqrt(codeComments) / (codeComments + complexityScore);
- } else {
- // Saturate at 1
- codeReviewBonus += 1;
- }
- return codeReviewBonus / 2 * maxBonus;
- }
}
diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/leaderboard/ScoringService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/leaderboard/ScoringService.java
new file mode 100644
index 00000000..9bfa7aaf
--- /dev/null
+++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/leaderboard/ScoringService.java
@@ -0,0 +1,129 @@
+package de.tum.in.www1.hephaestus.leaderboard;
+
+import de.tum.in.www1.hephaestus.gitprovider.issue.Issue;
+import de.tum.in.www1.hephaestus.gitprovider.issuecomment.IssueComment;
+import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest;
+import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository;
+import de.tum.in.www1.hephaestus.gitprovider.pullrequestreview.PullRequestReview;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ScoringService {
+
+ private final Logger logger = LoggerFactory.getLogger(ScoringService.class);
+
+ @Autowired
+ private PullRequestRepository pullRequestRepository;
+
+ public double WEIGHT_APPROVAL = 2.0;
+ public double WEIGHT_CHANGESREQUESTED = 2.5;
+ public double WEIGHT_COMMENT = 1.5;
+
+ public double calculateReviewScore(List pullRequestReviews) {
+ return calculateReviewScore(pullRequestReviews, 0);
+ }
+
+ public double calculateReviewScore(List pullRequestReviews, int numberOfIssueComments) {
+ // All reviews are for the same pull request
+ int complexityScore = calculateComplexityScore(pullRequestReviews.get(0).getPullRequest());
+
+ double approvalScore = pullRequestReviews
+ .stream()
+ .filter(review -> review.getState() == PullRequestReview.State.APPROVED)
+ .filter(review -> review.getAuthor().getId() != review.getPullRequest().getAuthor().getId())
+ .map(review -> WEIGHT_APPROVAL * calculateCodeReviewBonus(review.getComments().size(), complexityScore))
+ .reduce(0.0, Double::sum);
+
+ double changesRequestedScore = pullRequestReviews
+ .stream()
+ .filter(review -> review.getState() == PullRequestReview.State.CHANGES_REQUESTED)
+ .filter(review -> review.getAuthor().getId() != review.getPullRequest().getAuthor().getId())
+ .map(review -> WEIGHT_CHANGESREQUESTED * calculateCodeReviewBonus(review.getComments().size(), complexityScore))
+ .reduce(0.0, Double::sum);
+
+ double commentScore = pullRequestReviews
+ .stream()
+ .filter(review -> review.getState() == PullRequestReview.State.COMMENTED || review.getState() == PullRequestReview.State.UNKNOWN)
+ .filter(review -> review.getAuthor().getId() != review.getPullRequest().getAuthor().getId())
+ .map(review -> WEIGHT_COMMENT * calculateCodeReviewBonus(review.getComments().size(), complexityScore))
+ .reduce(0.0, Double::sum);
+
+ double issueCommentScore = WEIGHT_COMMENT * numberOfIssueComments;
+
+ double interactionScore = approvalScore + changesRequestedScore + commentScore + issueCommentScore;
+ return 10 * interactionScore * complexityScore / (interactionScore + complexityScore);
+ }
+
+ public double calculateReviewScore(IssueComment issueComment) {
+ Issue issue = issueComment.getIssue();
+ PullRequest pullRequest;
+ if (issue.isPullRequest()) {
+ pullRequest = (PullRequest) issue;
+ } else {
+ var optionalPR = pullRequestRepository.findByRepositoryAndNumber(issue.getRepository(), issue.getNumber());
+ if (optionalPR.isEmpty()) {
+ logger.error("Issue comment is not associated with a pull request.");
+ return 0;
+ }
+ pullRequest = optionalPR.get();
+ }
+
+ int complexityScore = calculateComplexityScore(pullRequest);
+
+ return 10 * WEIGHT_COMMENT * complexityScore / (WEIGHT_COMMENT + complexityScore);
+ }
+
+ /**
+ * Calculates the code review bonus for a given number of code comments and complexity score.
+ * The bonus is a value between 0 and 2.
+ * Taken from the original leaderboard implementation script.
+ *
+ * @param codeComments
+ * @param complexityScore
+ * @return bonus
+ */
+ private double calculateCodeReviewBonus(int codeComments, int complexityScore) {
+ double maxBonus = 2;
+
+ double codeReviewBonus = 1;
+ if (codeComments < complexityScore) {
+ // Function goes from 0 at codeComments = 0 to 1 at codeComments = complexityScore
+ codeReviewBonus += 2 * Math.sqrt(complexityScore) * Math.sqrt(codeComments) / (codeComments + complexityScore);
+ } else {
+ // Saturate at 1
+ codeReviewBonus += 1;
+ }
+ return codeReviewBonus / 2 * maxBonus;
+ }
+
+ /**
+ * Calculates the complexity score for a given pull request.
+ * Possible values: 1, 3, 7, 17, 33.
+ * Taken from the original leaderboard implementation script.
+ *
+ * @param pullRequest
+ * @return score
+ */
+ private int calculateComplexityScore(PullRequest pullRequest) {
+ Double complexityScore =
+ ((pullRequest.getChangedFiles() * 3) +
+ (pullRequest.getCommits() * 0.5) +
+ pullRequest.getAdditions() +
+ pullRequest.getDeletions()) /
+ 10;
+ if (complexityScore < 10) {
+ return 1; // Simple
+ } else if (complexityScore < 50) {
+ return 3; // Medium
+ } else if (complexityScore < 100) {
+ return 7; // Large
+ } else if (complexityScore < 500) {
+ return 17; // Huge
+ }
+ return 33; // Overly complex
+ }
+}
diff --git a/webapp/src/app/core/modules/openapi/model/pull-request-review-info.ts b/webapp/src/app/core/modules/openapi/model/pull-request-review-info.ts
index d9f4d53b..dba6c5f1 100644
--- a/webapp/src/app/core/modules/openapi/model/pull-request-review-info.ts
+++ b/webapp/src/app/core/modules/openapi/model/pull-request-review-info.ts
@@ -21,6 +21,7 @@ export interface PullRequestReviewInfo {
author?: UserInfo;
pullRequest?: PullRequestBaseInfo;
htmlUrl: string;
+ score: number;
submittedAt?: string;
}
export namespace PullRequestReviewInfo {
diff --git a/webapp/src/app/home/leaderboard/leaderboard.component.html b/webapp/src/app/home/leaderboard/leaderboard.component.html
index 05e98f37..c56bdadb 100644
--- a/webapp/src/app/home/leaderboard/leaderboard.component.html
+++ b/webapp/src/app/home/leaderboard/leaderboard.component.html
@@ -3,7 +3,10 @@
Rank
Contributor
-
Score
+
+
+ Score
+
Activity
diff --git a/webapp/src/app/home/leaderboard/leaderboard.component.ts b/webapp/src/app/home/leaderboard/leaderboard.component.ts
index ef046e95..ae43cd88 100644
--- a/webapp/src/app/home/leaderboard/leaderboard.component.ts
+++ b/webapp/src/app/home/leaderboard/leaderboard.component.ts
@@ -14,6 +14,8 @@ import { TableHeaderDirective } from 'app/ui/table/table-header.directive';
import { TableRowDirective } from 'app/ui/table/table-row.directive';
import { TableComponent } from 'app/ui/table/table.component';
import { ReviewsPopoverComponent } from './reviews-popover/reviews-popover.component';
+import { HlmIconComponent, provideIcons } from '@spartan-ng/ui-icon-helm';
+import { lucideAward } from '@ng-icons/lucide';
@Component({
selector: 'app-leaderboard',
@@ -29,8 +31,10 @@ import { ReviewsPopoverComponent } from './reviews-popover/reviews-popover.compo
TableRowDirective,
ReviewsPopoverComponent,
NgIconComponent,
+ HlmIconComponent,
RouterLink
],
+ providers: [provideIcons({ lucideAward })],
templateUrl: './leaderboard.component.html'
})
export class LeaderboardComponent {
diff --git a/webapp/src/app/home/leaderboard/legend/legend.component.html b/webapp/src/app/home/leaderboard/legend/legend.component.html
index 5f8c8eea..05667b27 100644
--- a/webapp/src/app/home/leaderboard/legend/legend.component.html
+++ b/webapp/src/app/home/leaderboard/legend/legend.component.html
@@ -36,14 +36,6 @@
Icons
more complex pull requests contribute more. The final score balances your interactions with the complexity of the work reviewed,
highlighting both your engagement and the difficulty of the tasks you've undertaken. This score reflects your impact but does not directly measure time invested or
work quality.
-
- [source]
-