Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General: Display total LLM cost in course detail view #10019

Merged
merged 8 commits into from
Dec 22, 2024

Conversation

wasnertobias
Copy link
Contributor

@wasnertobias wasnertobias commented Dec 14, 2024

Checklist

General

Server

Client

  • Important: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the principle of data economy for all client-server REST calls.
  • I strictly followed the client coding and design guidelines.
  • Following the theming guidelines, I specified colors only in the theming variable files and checked that the changes look consistent in both the light and the dark theme.
  • I added multiple integration tests (Jest) related to the features (with a high test coverage), while following the test guidelines.
  • I added multiple screenshots/screencasts of my UI changes.
  • I translated all newly inserted strings into English and German.

Motivation and Context

LLM usage of IRIS and Athena has been tracked for a few weeks but has not yet been shown anywhere to users. This should change, and as a first approach, the total LLM cost of a course should be shown in the course details. In a later approach (TBD), the design will be improved to display usage on specific days for a given course.

Description

Introduces the calculation and visualization of total LLM costs per course in course details view. Back-end changes include a repository query and DTO update, while front-end changes add a doughnut chart and i18n labels to display the cost. This enhances transparency of LLM usage.

Steps for Testing

Prerequisites:

  • 1 Instructor of course A
  • IRIS or Athena usage in course A is enabled
  1. Log in to Artemis
  2. Navigate to Course Details view
  3. See that "Total LLM Cost" is displayed
  4. Do use IRIS or Athena in this course
  5. See that the "Total LLM Cost" increased

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Review Progress

Performance Review

  • I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students.
  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Performance Tests

  • Test 1
  • Test 2

Test Coverage

Screenshots

image

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new metric for tracking the total LLM cost in euros within course management details.
    • Introduced a new doughnut chart to visualize the current LLM cost in the course detail view.
  • Bug Fixes

    • Enhanced conditional rendering logic for displaying course statistics to ensure accuracy.
  • Localization

    • Added new localization keys for displaying total LLM cost in both German and English.
  • Tests

    • Expanded test coverage for the CourseDetailDoughnutChartComponent to include scenarios for the new LLM cost metric.

Introduces the calculation and visualization of total LLM costs per course. Back-end changes include a repository query and DTO update, while front-end changes add a doughnut chart and i18n labels to display the cost. This enhances transparency of LLM usage.
@wasnertobias wasnertobias added feature ready for review server Pull requests that update Java code. (Added Automatically!) client Pull requests that update TypeScript code. (Added Automatically!) small user interface server-api Improves the server API labels Dec 14, 2024
@wasnertobias wasnertobias requested a review from krusche December 14, 2024 11:33
@wasnertobias wasnertobias requested a review from a team as a code owner December 14, 2024 11:33
@github-actions github-actions bot added the tests label Dec 14, 2024
@github-actions github-actions bot added the core Pull requests that affect the corresponding module label Dec 14, 2024
@wasnertobias wasnertobias changed the title Development: Display total LLM cost in course detail view Development: Display total LLM cost in course detail view Dec 14, 2024
Copy link

coderabbitai bot commented Dec 14, 2024

Walkthrough

This pull request introduces functionality to track and display the total LLM (Large Language Model) cost for a course. The changes span multiple files across the backend and frontend, adding a new field currentTotalLlmCostInEur to the course management detail view. A new repository method calculates the total LLM cost, which is then integrated into the course service and exposed through the frontend components. The implementation includes updates to data transfer objects, repositories, services, and user interface components to support this new metric.

Changes

File Change Summary
src/main/java/de/tum/cit/aet/artemis/core/dto/CourseManagementDetailViewDTO.java Added currentTotalLlmCostInEur field to record
src/main/java/de/tum/cit/aet/artemis/core/repository/LLMTokenUsageTraceRepository.java Added calculateTotalLlmCostInEurForCourse method
src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java Updated to include LLM token usage repository and calculate total LLM cost
src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts Added currentTotalLlmCostInEur property
src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html Modified rendering logic for doughnut chart based on new data
src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts Added showText input property and updated logic for pie chart
src/main/webapp/app/course/manage/detail/course-detail.component.html Added <jhi-course-detail-doughnut-chart> component for LLM cost
src/main/webapp/app/course/manage/detail/course-detail.component.ts Added CURRENT_LLM_COST enum value
src/main/webapp/i18n/de/course.json Added German localization for LLM cost title
src/main/webapp/i18n/en/course.json Added English localization for LLM cost title
src/test/javascript/spec/component/course/detail/course-detail-doughnut-chart.component.spec.ts Added test case for CURRENT_LLM_COST
src/test/javascript/spec/component/course/detail/course-detail.component.spec.ts Added currentTotalLlmCostInEur property to dtoMock

Suggested Labels

component:LTI, ready to merge, bugfix

Suggested Reviewers

  • JohannesStoehr
  • SimonEntholzer
  • az108
  • pzdr7
  • florian-glombik
  • krusche

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de75832 and 8bd072a.

⛔ Files ignored due to path filters (2)
  • src/main/resources/config/liquibase/changelog/20241217150008_changelog.xml is excluded by !**/*.xml
  • src/main/resources/config/liquibase/master.xml is excluded by !**/*.xml
📒 Files selected for processing (3)
  • src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html (1 hunks)
  • src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts (3 hunks)
  • src/main/webapp/app/course/manage/detail/course-detail.component.html (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/webapp/app/course/manage/detail/course-detail.component.html
  • src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html
  • src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (5)
src/main/java/de/tum/cit/aet/artemis/core/dto/CourseManagementDetailViewDTO.java (1)

11-11: Consider using primitive double for cost field.

The implementation looks good, but since LLM cost should never be null (defaulting to 0.0), consider using primitive double instead of Double to improve memory efficiency and prevent potential NPEs.

src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts (1)

29-31: LGTM! Consider future LLM metrics.

Good organization with the "LLM Stats" section comment. This structure will make it easier to add more LLM-related metrics in the future if needed.

src/main/java/de/tum/cit/aet/artemis/core/repository/LLMTokenUsageTraceRepository.java (1)

18-19: Consider using BigDecimal for financial calculations.

The current implementation using floating-point arithmetic might lead to precision issues. Consider moving the calculation to the service layer using BigDecimal for more accurate financial computations.

src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts (1)

Line range hint 53-59: Consider empty string edge case in ngOnChanges

The condition !this.showText will evaluate to true for empty strings, which might lead to unintended fallback behavior.

Consider using strict undefined check:

-if (this.currentAbsolute == undefined && !this.receivedStats && !this.showText) {
+if (this.currentAbsolute == undefined && !this.receivedStats && this.showText === undefined) {
src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java (1)

233-233: Consider breaking down the service dependencies

The constructor has a large number of dependencies (20+ parameters). Consider breaking this service into smaller, more focused services to improve maintainability and follow the Single Responsibility Principle.

Consider creating separate services for specific functionality domains, for example:

  • CourseEnrollmentService
  • CourseStatisticsService
  • CourseArchiveService
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1802aa0 and f217a6c.

📒 Files selected for processing (12)
  • src/main/java/de/tum/cit/aet/artemis/core/dto/CourseManagementDetailViewDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/repository/LLMTokenUsageTraceRepository.java (2 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java (6 hunks)
  • src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts (1 hunks)
  • src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html (1 hunks)
  • src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts (3 hunks)
  • src/main/webapp/app/course/manage/detail/course-detail.component.html (1 hunks)
  • src/main/webapp/app/course/manage/detail/course-detail.component.ts (1 hunks)
  • src/main/webapp/i18n/de/course.json (1 hunks)
  • src/main/webapp/i18n/en/course.json (1 hunks)
  • src/test/javascript/spec/component/course/detail/course-detail-doughnut-chart.component.spec.ts (1 hunks)
  • src/test/javascript/spec/component/course/detail/course-detail.component.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/main/webapp/app/course/manage/detail/course-detail.component.ts
  • src/main/webapp/i18n/de/course.json
🧰 Additional context used
📓 Path-based instructions (9)
src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/course/detail/course-detail.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/course/detail/course-detail-doughnut-chart.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/course/manage/detail/course-detail.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/java/de/tum/cit/aet/artemis/core/repository/LLMTokenUsageTraceRepository.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/java/de/tum/cit/aet/artemis/core/dto/CourseManagementDetailViewDTO.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

🔇 Additional comments (10)
src/main/java/de/tum/cit/aet/artemis/core/repository/LLMTokenUsageTraceRepository.java (1)

6-7: LGTM! Clean import organization.

The imports are well-organized and specific (no wildcard imports).

src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html (2)

12-15: LGTM! Proper usage of new Angular syntax.

The change from *ngIf to @if follows the new Angular syntax guidelines correctly.


12-20: Consider handling edge cases in the conditional logic.

The current implementation might have edge cases:

  1. When currentPercentage is 0, it might be treated as undefined
  2. When showText is an empty string, it might lead to undesired UI state

Consider adding explicit null checks and handling empty strings.

-            @if (receivedStats && currentPercentage != undefined) {
+            @if (receivedStats && currentPercentage !== undefined && currentPercentage !== null) {
                <div class="h4">{{ currentPercentage }}%</div>
            } @else if (receivedStats && showText) {
-                <div class="h4">{{ showText }}</div>
+                <div class="h4">{{ showText || '' }}</div>
            }
src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts (2)

24-24: LGTM: Input property correctly typed

The new input property showText is properly typed as string | undefined.


88-91: LGTM: LLM cost chart configuration

The new chart type is properly configured with appropriate title and undefined link.

src/main/webapp/i18n/en/course.json (1)

262-263: LGTM: Translation key properly added

The new translation key follows the existing naming convention and provides a clear, concise description.

src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java (4)

86-86: LGTM!

The import statement follows Java conventions and is properly organized within the core repository imports section.


218-219: LGTM!

The field declaration follows Spring best practices with proper encapsulation and constructor injection pattern.


277-277: LGTM!

The field initialization follows the established pattern and is properly placed within the constructor.


861-862: Verify LLM cost calculation implementation

The LLM cost calculation is delegated to the repository layer. Let's verify the implementation of calculateTotalLlmCostInEurForCourse to ensure proper cost calculation.

✅ Verification successful

LLM cost calculation implementation is properly implemented

The repository method calculateTotalLlmCostInEurForCourse is correctly implemented with:

  • A proper SQL query that calculates costs based on both input and output tokens
  • Cost calculation using token counts and their respective costs per million tokens
  • Proper rounding to 2 decimal places
  • Null-safe implementation returning Double type
  • Integration with the course management view and UI components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the implementation of LLM cost calculation in the repository
# Expected: Find the repository method implementation and any related tests

# Search for the repository method implementation
echo "Searching for repository method implementation..."
ast-grep --pattern 'calculateTotalLlmCostInEurForCourse($id) {
  $$$
}'

# Search for related test cases
echo "Searching for related test cases..."
rg -l "calculateTotalLlmCostInEurForCourse.*Test"

Length of output: 315


Script:

#!/bin/bash
# Let's try a different approach to find the repository implementation

# First, let's find the repository interface or class
echo "Searching for LlmTokenUsageTraceRepository..."
rg -l "LlmTokenUsageTraceRepository"

# Search for any method containing calculateTotalLlmCostInEur
echo "Searching for cost calculation method..."
rg "calculateTotalLlmCostInEur" -A 5

# Look for related test files
echo "Searching for related test files..."
fd -e java -e kt test.*LlmTokenUsageTrace

Length of output: 1769


Script:

#!/bin/bash
# Let's check the repository implementation details

# Look for SQL query or implementation details
echo "Searching for SQL query or implementation..."
rg -B 5 -A 1 "calculateTotalLlmCostInEurForCourse" --type java

# Check if there's any @Query annotation associated
echo "Searching for @Query annotation..."
rg -B 3 "calculateTotalLlmCostInEurForCourse" --type java

# Look for any cost-related constants or configurations
echo "Searching for cost-related configurations..."
rg "llm.*cost|cost.*llm" -i

Length of output: 8018

Malekos74
Malekos74 previously approved these changes Dec 14, 2024
Copy link

@Malekos74 Malekos74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on server 5

Works as expected

Copy link

⚠️ Unable to deploy to test servers ⚠️

Testserver "artemis-test3.artemis.cit.tum.de" is already in use by PR #9851.

Copy link

@JerroyTan JerroyTan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Tested on TS1]

Tested with the IRIS setting of monitoring build failures
image
Submitted multiple build failures, but cost of LLM did not go up. (possibly because monitoring build failures does not incur that much of cost?)

@wasnertobias wasnertobias dismissed stale reviews from HawKhiem, sachmii, and Malekos74 via 4bc3d38 December 17, 2024 14:08
@github-actions github-actions bot added the database Pull requests that update the database. (Added Automatically!). Require a CRITICAL deployment. label Dec 17, 2024
coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 17, 2024
Copy link

⚠️ Unable to deploy to test servers ⚠️

Testserver "artemis-test3.artemis.cit.tum.de" is already in use by PR #9921.

@github-actions github-actions bot added the deployment-error Added by deployment workflows if an error occured label Dec 21, 2024
@krusche krusche changed the title Development: Display total LLM cost in course detail view Genera: Display total LLM cost in course detail view Dec 22, 2024
@krusche krusche changed the title Genera: Display total LLM cost in course detail view General: Display total LLM cost in course detail view Dec 22, 2024
@krusche krusche merged commit dd94f37 into develop Dec 22, 2024
23 of 28 checks passed
@krusche krusche deleted the feature/course-details-show-llm-usage branch December 22, 2024 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!) core Pull requests that affect the corresponding module database Pull requests that update the database. (Added Automatically!). Require a CRITICAL deployment. deployment-error Added by deployment workflows if an error occured feature ready for review server Pull requests that update Java code. (Added Automatically!) server-api Improves the server API small tests user interface
Projects
Status: Merged
Development

Successfully merging this pull request may close these issues.

10 participants