-
Notifications
You must be signed in to change notification settings - Fork 0
Add average daily activity calculation with command and REST API #69
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
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…deprecation Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements a new feature to calculate and expose average player activity over a configurable time period (default: 7 days). The implementation adds both a command interface and a REST API endpoint.
Key changes:
- Core calculation methods in
ActivityRecordServiceto compute average daily activity and total hours within a period /at average [playerName] [days]command for in-game access with appropriate permission (at.average)GET /api/average-daily-activity/{uuid}?days=XREST endpoint with comprehensive error handling
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/main/java/dansplugins/activitytracker/services/ActivityRecordService.java |
Adds calculateAverageDailyActivity and calculateTotalHoursInPeriod methods with date-based session filtering |
src/main/java/dansplugins/activitytracker/commands/AverageCommand.java |
New command handler supporting player name and days parameters with UUID lookup and formatted output |
src/main/java/dansplugins/activitytracker/api/RestApiService.java |
New REST endpoint with UUID validation, query parameter parsing, and error responses |
src/main/java/dansplugins/activitytracker/api/AverageDailyActivityResponse.java |
Response DTO containing player info, period, average and total hours |
src/main/java/dansplugins/activitytracker/ActivityTracker.java |
Registers the new AverageCommand with the command service |
src/main/java/dansplugins/activitytracker/commands/HelpCommand.java |
Documents the new average command in help text |
src/main/resources/plugin.yml |
Adds at.average permission with default: true |
src/test/java/dansplugins/activitytracker/services/AverageDailyActivityTest.java |
Unit tests for edge cases (null records, invalid days, no sessions) |
src/test/java/dansplugins/activitytracker/api/AverageDailyActivityResponseTest.java |
Tests for response DTO serialization and getters |
REST_API.md |
Comprehensive documentation with endpoint details and usage examples in curl, JavaScript, and Python |
openapi.yaml |
OpenAPI 3.0 specification for the new endpoint with schema definitions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @Test | ||
| public void testCalculateAverageDailyActivity_WithCompletedSessions() { | ||
| UUID playerUuid = UUID.randomUUID(); | ||
| Session session = sessionFactory.createSession(playerUuid); | ||
| ActivityRecord record = new ActivityRecord(playerUuid, session); | ||
|
|
||
| // Clear the initial session | ||
| record.getSessions().clear(); | ||
|
|
||
| // We can't easily test with real sessions without mocking LocalDateTime | ||
| // So we just verify the method doesn't crash with a valid record | ||
| double average = activityRecordService.calculateAverageDailyActivity(record, 7); | ||
|
|
||
| // Should be 0 since there are no sessions | ||
| assertEquals(0.0, average, 0.01); | ||
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test doesn't effectively validate the calculation logic with actual data. The test creates a record, clears all sessions, and then verifies the result is 0.0, which is essentially the same as the "NoSessions" test. Consider adding a test that creates sessions with specific timestamps and validates that the calculation correctly filters sessions based on the cutoff date and properly sums the hours.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5d4b940. The test now creates sessions with specific timestamps (3, 5, and 10 days ago) and validates that:
- Sessions within the 7-day period (3 and 5 days ago) are correctly included
- Sessions outside the period (10 days ago) are excluded
- Total hours and averages are calculated accurately for both 7-day and 30-day periods
| Spark.get("/api/average-daily-activity/:uuid", (request, response) -> { | ||
| String uuidParam = request.params(":uuid"); | ||
| UUID playerUuid; | ||
|
|
||
| try { | ||
| playerUuid = UUID.fromString(uuidParam); | ||
| } catch (IllegalArgumentException e) { | ||
| response.status(400); | ||
| return gson.toJson(new ErrorResponse("Invalid UUID format")); | ||
| } | ||
|
|
||
| ActivityRecord record = persistentData.getActivityRecord(playerUuid); | ||
| if (record == null) { | ||
| response.status(404); | ||
| return gson.toJson(new ErrorResponse("Player not found")); | ||
| } | ||
|
|
||
| // Get days parameter from query string (default: 7) | ||
| String daysParam = request.queryParams("days"); | ||
| int days = 7; | ||
| if (daysParam != null) { | ||
| try { | ||
| days = Integer.parseInt(daysParam); | ||
| if (days <= 0) { | ||
| response.status(400); | ||
| return gson.toJson(new ErrorResponse("Days must be a positive number")); | ||
| } | ||
| } catch (NumberFormatException e) { | ||
| response.status(400); | ||
| return gson.toJson(new ErrorResponse("Invalid days parameter")); | ||
| } | ||
| } | ||
|
|
||
| UUIDChecker uuidChecker = new UUIDChecker(); | ||
| String playerName = uuidChecker.findPlayerNameBasedOnUUID(playerUuid); | ||
| if (playerName == null) { | ||
| playerName = "Unknown"; | ||
| } | ||
|
|
||
| double averageHoursPerDay = activityRecordService.calculateAverageDailyActivity(record, days); | ||
| double totalHours = activityRecordService.calculateTotalHoursInPeriod(record, days); | ||
|
|
||
| AverageDailyActivityResponse avgResponse = new AverageDailyActivityResponse( | ||
| playerUuid.toString(), | ||
| playerName, | ||
| days, | ||
| averageHoursPerDay, | ||
| totalHours | ||
| ); | ||
|
|
||
| return gson.toJson(avgResponse); | ||
| }); |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The REST API endpoint for average daily activity lacks test coverage. The RestApiServiceTest file contains tests for other endpoints (stats, leaderboard, player data), but there are no tests for the new average-daily-activity endpoint. Consider adding tests to verify the endpoint handles valid requests, invalid UUIDs, missing players, and invalid days parameters correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5d4b940. Added 5 comprehensive tests covering:
- Response serialization/deserialization
- Valid data retrieval for average daily activity
- Invalid days parameter handling (0, negative)
- Player not found scenario
- Different time periods (7 days vs 30 days)
| double totalHours = 0.0; | ||
|
|
||
| // Sum up hours from sessions within the time period | ||
| for (Session session : record.getSessions()) { |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The date filtering logic uses 'not isBefore' to include sessions on or after the cutoff date. While this works correctly for including sessions from exactly N days ago, it might be clearer to use 'isAfter or isEqual' semantics. However, since LocalDateTime doesn't have an isAfterOrEqual method, consider using 'isAfter(cutoffDate.minusNanos(1))' or add a comment explaining that '!isBefore(cutoffDate)' means 'on or after cutoffDate' to improve code readability.
| for (Session session : record.getSessions()) { | |
| for (Session session : record.getSessions()) { | |
| // !isBefore(cutoffDate) means "on or after cutoffDate" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5d4b940. Added clarifying comment: // !isBefore(cutoffDate) means "on or after cutoffDate" - includes sessions from exactly N days ago
| sender.sendMessage(ChatColor.RED + "Invalid number of days. Using default: " + DEFAULT_DAYS); | ||
| days = DEFAULT_DAYS; |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a NumberFormatException is caught for invalid days parameter, the code sends an error message but then continues execution with the default value instead of returning false. This creates inconsistent behavior - the error message says the days parameter is invalid, but the command succeeds anyway. Consider returning false after sending the error message to maintain consistency with other validation errors in the same method.
| sender.sendMessage(ChatColor.RED + "Invalid number of days. Using default: " + DEFAULT_DAYS); | |
| days = DEFAULT_DAYS; | |
| sender.sendMessage(ChatColor.RED + "Invalid number of days."); | |
| return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5d4b940. Changed to return false immediately after sending the error message, maintaining consistency with other validation errors.
|
@copilot address comments |
… handling Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Average Daily Activity Feature - COMPLETE ✅
All requirements from the issue have been successfully implemented and tested.
✅ Features Implemented
Command Interface:
/at average [playerName] [days]at.average(default: true)REST API Endpoint:
GET /api/average-daily-activity/{uuid}?days=XAccurate Calculation
!isBefore()for inclusive date comparison (with clarifying comment)📝 Changes Summary
Code Improvements:
Test Coverage:
🔒 Security & Quality
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.