Skip to content

Commit 95a51c5

Browse files
Merge branch 'main' into watchtower-cleanup
2 parents 24c191b + 201ad49 commit 95a51c5

File tree

20 files changed

+158
-125
lines changed

20 files changed

+158
-125
lines changed

.github/workflows/docker-publish.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ env:
1111

1212
jobs:
1313
build-and-push-image:
14-
runs-on: ubuntu-latest
14+
runs-on: ubuntu-24.04-arm
1515
permissions:
1616
contents: read
1717
packages: write
@@ -25,10 +25,6 @@ jobs:
2525
uses: actions/setup-node@v3
2626
with:
2727
node-version: 22 # Compatible Node.js version
28-
29-
-
30-
name: Set up QEMU
31-
uses: docker/setup-qemu-action@v3
3228

3329
-
3430
name: Set up Docker Buildx

docs/getting-started/db-insert-statements/insert-rubric-database.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ The primary things that needs to be updated on an ongoing basis are:
1010

1111
## Statements
1212
> [!NOTE]
13-
> Updated as of **SUMMER 2024**
13+
> Updated as of **WINTER 2026**
1414
1515
```mysql
1616
INSERT INTO rubric_config (phase, type, category, criteria, points, rubric_id) VALUES
1717
('GitHub', 'GITHUB_REPO', 'GitHub Repository', 'Two Git commits: one for creating the repository and another for `notes.md`.', 15, '_6829'),
1818
('Phase0', 'GIT_COMMITS', 'Git Commits', 'Necessary commit amount', 0, '90342_649'),
1919
('Phase0', 'PASSOFF_TESTS', 'Functionality', 'All pass off test cases succeed', 125, '_1958'),
2020
('Phase1', 'GIT_COMMITS', 'Git Commits', 'Necessary commit amount', 0, '90342_7800'),
21+
('Phase1', 'EXTRA_CREDIT', 'Extra Credit', 'Castling and En Passant', 10, '90342_7835'),
2122
('Phase1', 'PASSOFF_TESTS', 'Functionality', 'All pass off test cases succeed', 125, '_1958'),
2223
('Phase3', 'GIT_COMMITS', 'Git Commits', 'Necessary commit amount', 0, '90344_2520'),
2324
('Phase3', 'PASSOFF_TESTS', 'Web API Works', 'All pass off test cases in StandardAPITests.java succeed', 125, '_5202'),

docs/getting-started/db-insert-statements/update-rubric-database.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ The primary things that needs to be updated on an ongoing basis are:
1313

1414
## Statements
1515
> [!NOTE]
16-
> Updated as of **SUMMER 2024**
16+
> Updated as of **WINTER 2026**
17+
18+
```mysql
19+
INSERT INTO rubric_config (phase, type, category, criteria, points, rubric_id)
20+
VALUES ('Phase1', 'EXTRA_CREDIT', 'Extra Credit', 'Castling and En Passant', 10, '90342_7835')
21+
```
22+
23+
If updating prior to **SUMMER 2024**, run these statements before running the ones above.
1724

1825
```mysql
1926
ALTER TABLE rubric_config ADD rubric_id VARCHAR(15);

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "autograder",
3+
"version": "0.0.0",
4+
"private": true,
5+
"workspaces": [
6+
"src/main/resources/frontend"
7+
],
8+
"scripts": {
9+
"frontend": "npm run start -w src/main/resources/frontend",
10+
"frontend:host": "npm run start:host -w src/main/resources/frontend"
11+
}
12+
}

src/main/java/edu/byu/cs/autograder/Grader.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import edu.byu.cs.autograder.score.LateDayCalculator;
1111
import edu.byu.cs.autograder.quality.QualityGrader;
1212
import edu.byu.cs.autograder.score.Scorer;
13+
import edu.byu.cs.autograder.test.ExtraCreditGrader;
1314
import edu.byu.cs.autograder.test.PassoffTestGrader;
1415
import edu.byu.cs.autograder.test.PreviousPhasePassoffTestGrader;
1516
import edu.byu.cs.autograder.test.UnitTestGrader;
@@ -133,6 +134,7 @@ private Rubric evaluateProject(RubricConfig rubricConfig, CommitVerificationResu
133134
// This code is violating the open-closed principle.
134135
case PASSOFF_TESTS -> new PassoffTestGrader(gradingContext).runTests();
135136
case UNIT_TESTS -> new UnitTestGrader(gradingContext).runTests();
137+
case EXTRA_CREDIT -> new ExtraCreditGrader(gradingContext).runTests();
136138
case QUALITY -> new QualityGrader(gradingContext).runQualityChecks();
137139
case GITHUB_REPO -> new GitHubAssignmentGrader().grade(commitVerificationResult);
138140
case GIT_COMMITS, GRADING_ISSUE -> null;

src/main/java/edu/byu/cs/autograder/score/Scorer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,11 @@ private ScorePair getScores(Rubric rubric) throws GradingException, DataAccessEx
417417
throw new GradingException("Total possible points for phase " + gradingContext.phase() + " is 0");
418418
}
419419

420+
if (DaoService.getRubricConfigDao().getRubricConfig(gradingContext.phase()) instanceof RubricConfig rubricConfig &&
421+
rubricConfig.items().get(Rubric.RubricType.EXTRA_CREDIT) instanceof RubricConfig.RubricConfigItem item) {
422+
totalPossiblePoints -= item.points();
423+
}
424+
420425
float score = 0;
421426
float rawScore = 0;
422427
for (Rubric.RubricType type : Rubric.RubricType.values()) {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package edu.byu.cs.autograder.test;
2+
3+
import edu.byu.cs.autograder.GradingContext;
4+
import edu.byu.cs.autograder.GradingException;
5+
import edu.byu.cs.model.Rubric;
6+
import edu.byu.cs.model.TestNode;
7+
import edu.byu.cs.model.TestOutput;
8+
import edu.byu.cs.util.PhaseUtils;
9+
10+
import java.io.File;
11+
import java.util.*;
12+
13+
/**
14+
* Runs and scores the extra credit tests for the phase a submission is graded for
15+
*/
16+
public class ExtraCreditGrader extends TestGrader {
17+
public ExtraCreditGrader(GradingContext gradingContext) {
18+
super(gradingContext);
19+
}
20+
21+
@Override
22+
protected String name() {
23+
return "extra credit";
24+
}
25+
26+
@Override
27+
protected Set<File> testsToCompile() {
28+
return Set.of(phaseTests);
29+
}
30+
31+
@Override
32+
protected Set<String> packagesToTest() throws GradingException {
33+
return PhaseUtils.extraCreditPackagesToTest(gradingContext.phase());
34+
}
35+
36+
@Override
37+
protected String testName() {
38+
return "Extra Credit";
39+
}
40+
41+
@Override
42+
protected float getScore(TestOutput testOutput) {
43+
TestNode testResults = testOutput.root();
44+
float score = 0;
45+
46+
for (TestNode child : testResults.getChildren().values()) {
47+
score += (float) Math.floor((float) child.getNumTestsPassed() /
48+
(child.getNumTestsPassed() + child.getNumTestsFailed()));
49+
}
50+
51+
return score / testResults.getChildren().size();
52+
}
53+
54+
@Override
55+
protected String getNotes(TestOutput testOutput) {
56+
if (testOutput.root() == null) return "No tests were run";
57+
58+
return "Extra credit tests: +" + (getScore(testOutput) * PhaseUtils.extraCreditScore(gradingContext.phase()));
59+
}
60+
61+
@Override
62+
protected Rubric.RubricType rubricType() {
63+
return Rubric.RubricType.EXTRA_CREDIT;
64+
}
65+
66+
@Override
67+
protected Set<String> modulesToCheckCoverage() {
68+
return Set.of();
69+
}
70+
}

src/main/java/edu/byu/cs/autograder/test/PassoffTestGrader.java

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ protected Set<String> packagesToTest() throws GradingException {
3434
return PhaseUtils.passoffPackagesToTest(gradingContext.phase());
3535
}
3636

37-
@Override
38-
protected Set<String> extraCreditTests() {
39-
return PhaseUtils.extraCreditTests(gradingContext.phase());
40-
}
41-
4237
@Override
4338
protected String testName() {
4439
return "Passoff Tests";
@@ -48,25 +43,10 @@ protected String testName() {
4843
protected float getScore(TestOutput testOutput) {
4944
TestNode testResults = testOutput.root();
5045
float totalStandardTests = testResults.getNumTestsFailed() + testResults.getNumTestsPassed();
51-
TestNode extraCredit = testOutput.extraCredit();
52-
float totalECTests = extraCredit != null ? extraCredit.getNumTestsPassed() + extraCredit.getNumTestsFailed() : 0f;
5346

5447
if (totalStandardTests == 0) return 0;
5548

56-
float score = testResults.getNumTestsPassed() / totalStandardTests;
57-
if (totalECTests == 0) return score;
58-
59-
// extra credit calculation
60-
if (score < 1f) return score;
61-
Map<String, Float> ecScores = getECScores(extraCredit);
62-
float extraCreditValue = PhaseUtils.extraCreditValue(gradingContext.phase());
63-
for (String category : extraCreditTests()) {
64-
if (ecScores.get(category) == 1f) {
65-
score += extraCreditValue;
66-
}
67-
}
68-
69-
return score;
49+
return testResults.getNumTestsPassed() / totalStandardTests;
7050
}
7151

7252
@Override
@@ -83,11 +63,6 @@ protected String getNotes(TestOutput testOutput) {
8363
else {
8464
notes.append(testResults.getNumTestsFailed() + "/" + totalRequiredTests + " required tests failed");
8565
}
86-
Map<String, Float> ecScores = getECScores(testOutput.extraCredit());
87-
float extraCreditValue = PhaseUtils.extraCreditValue(gradingContext.phase());
88-
float totalECPoints = ecScores.values().stream().reduce(0f, (f1, f2) -> (float) (f1 + Math.floor(f2))) * extraCreditValue;
89-
90-
if (totalECPoints > 0f) notes.append("\nExtra credit tests: +").append(totalECPoints * 100).append("%");
9166

9267
return notes.toString();
9368
}
@@ -97,28 +72,6 @@ protected Rubric.RubricType rubricType() {
9772
return Rubric.RubricType.PASSOFF_TESTS;
9873
}
9974

100-
101-
private Map<String, Float> getECScores(TestNode results) {
102-
Map<String, Float> scores = new HashMap<>();
103-
if(results == null) return scores;
104-
105-
Queue<TestNode> unchecked = new PriorityQueue<>();
106-
unchecked.add(results);
107-
108-
while (!unchecked.isEmpty()) {
109-
TestNode node = unchecked.remove();
110-
for (TestNode child : node.getChildren().values()) {
111-
if (child.getEcCategory() != null) {
112-
scores.put(child.getEcCategory(), (float) child.getNumTestsPassed() /
113-
(child.getNumTestsPassed() + child.getNumTestsFailed()));
114-
unchecked.remove(child);
115-
} else unchecked.add(child);
116-
}
117-
}
118-
119-
return scores;
120-
}
121-
12275
@Override
12376
protected Set<String> modulesToCheckCoverage() {
12477
return Set.of();

src/main/java/edu/byu/cs/autograder/test/PreviousPhasePassoffTestGrader.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ protected Set<String> packagesToTest() throws GradingException {
3939
}
4040

4141
@Override
42-
protected Set<String> extraCreditTests() throws GradingException {
42+
protected Set<String> ignoredTests() throws GradingException {
4343
return allPreviousPhases(PhaseUtils::extraCreditTests);
4444
}
4545

@@ -84,7 +84,6 @@ protected String testName() {
8484
@Override
8585
protected float getScore(TestOutput testResults) throws GradingException {
8686
if (testResults.root().getNumTestsFailed() == 0) return 1f;
87-
testResults = new TestOutput(testResults.root(), null, testResults.coverage(), testResults.error());
8887
StringBuilder errorBuilder = new StringBuilder(ERROR_MESSAGE).append(" \nFailing tests: \n");
8988
failingTests(testResults.root(), errorBuilder);
9089
Rubric.Results results = Rubric.Results.testError(errorBuilder.toString(), testResults);

src/main/java/edu/byu/cs/autograder/test/TestAnalyzer.java

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,19 @@
1414
* <b>Important: this class is ONLY compatible with the xml output on the JUnit standalone client</b><br/>
1515
*/
1616
public class TestAnalyzer {
17-
18-
public record TestAnalysis(TestNode root, TestNode extraCredit) {}
19-
2017
/**
2118
* Parses the output of the JUnit Console Runner
2219
*
2320
* @param junitXmlOutput file containing test output
24-
* @param extraCreditTests the names of the test files (excluding .java) worth bonus points. This cannot be null, but can be empty
21+
* @param ignoredTests the names of the test files (excluding .java) to ignore. This cannot be null, but can be empty
2522
* @return the root of the test tree
2623
*/
27-
public TestAnalysis parse(File junitXmlOutput, Set<String> extraCreditTests) throws GradingException {
24+
public TestNode parse(File junitXmlOutput, Set<String> ignoredTests) throws GradingException {
2825
TestNode root = new TestNode();
2926
root.setTestName("JUnit Jupiter");
30-
TestNode extraCredit = new TestNode();
31-
extraCredit.setTestName("JUnit Jupiter Extra Credit");
3227

3328
if(!junitXmlOutput.exists()) {
34-
return compileAnalysis(root, extraCredit);
29+
return compileAnalysis(root);
3530
}
3631

3732
String xml = FileUtils.readStringFromFile(junitXmlOutput);
@@ -44,14 +39,9 @@ public TestAnalysis parse(File junitXmlOutput, Set<String> extraCreditTests) thr
4439

4540
int uniqueTestIndex = 0;
4641
for (TestSuite.TestCase testCase : suite.getTestcase()) {
47-
TestNode base = root;
48-
String ecCategory = null;
49-
for (String category : extraCreditTests) {
50-
if (testCase.getClassname().endsWith(category)) {
51-
ecCategory = category;
52-
base = extraCredit;
53-
break;
54-
}
42+
if (ignoredTests.stream().anyMatch(
43+
category -> testCase.getClassname().endsWith(category))) {
44+
continue;
5545
}
5646

5747
String name = testCase.getName();
@@ -66,7 +56,7 @@ public TestAnalysis parse(File junitXmlOutput, Set<String> extraCreditTests) thr
6656

6757
TestNode node = new TestNode();
6858
node.setTestName(name);
69-
TestNode parent = nodeForClass(base, testCase.getClassname());
59+
TestNode parent = nodeForClass(root, testCase.getClassname());
7060
String uniqueTestNameKey = String.format("%s - %d", name, uniqueTestIndex++);
7161
parent.getChildren().put(uniqueTestNameKey, node);
7262

@@ -75,13 +65,9 @@ public TestAnalysis parse(File junitXmlOutput, Set<String> extraCreditTests) thr
7565
node.setErrorMessage(testCase.getFailure().getData());
7666
}
7767

78-
if (ecCategory != null) {
79-
node.setEcCategory(ecCategory);
80-
parent.setEcCategory(ecCategory);
81-
}
8268
}
8369

84-
return compileAnalysis(root, extraCredit);
70+
return compileAnalysis(root);
8571
}
8672

8773
private TestNode nodeForClass(TestNode base, String name) {
@@ -102,11 +88,9 @@ private TestNode nodeForClass(TestNode base, String name) {
10288
else return nodeForClass(node, extra);
10389
}
10490

105-
private TestAnalysis compileAnalysis(TestNode root, TestNode extraCredit) {
91+
private TestNode compileAnalysis(TestNode root) {
10692
TestNode.collapsePackages(root);
10793
TestNode.countTests(root);
108-
TestNode.collapsePackages(extraCredit);
109-
TestNode.countTests(extraCredit);
110-
return new TestAnalysis(root, extraCredit);
94+
return root;
11195
}
11296
}

0 commit comments

Comments
 (0)