diff --git a/.idea/misc.xml b/.idea/misc.xml
index ba0541a..447794c 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/src/main/java/edu/kit/kastel/sdq/intelligrade/extensions/guis/BacklogPanel.java b/src/main/java/edu/kit/kastel/sdq/intelligrade/extensions/guis/BacklogPanel.java
new file mode 100644
index 0000000..d20e2a1
--- /dev/null
+++ b/src/main/java/edu/kit/kastel/sdq/intelligrade/extensions/guis/BacklogPanel.java
@@ -0,0 +1,167 @@
+/* Licensed under EPL-2.0 2024. */
+package edu.kit.kastel.sdq.intelligrade.extensions.guis;
+
+import java.awt.event.ActionEvent;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.event.DocumentEvent;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.SearchTextField;
+import com.intellij.ui.components.JBCheckBox;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.components.JBPanel;
+import edu.kit.kastel.sdq.artemis4j.grading.ProgrammingSubmission;
+import edu.kit.kastel.sdq.intelligrade.state.PluginState;
+import edu.kit.kastel.sdq.intelligrade.utils.ArtemisUtils;
+import net.miginfocom.swing.MigLayout;
+import org.jetbrains.annotations.NotNull;
+
+public class BacklogPanel extends JPanel {
+ private final SearchTextField searchField;
+ private final JBCheckBox showFirstRound;
+ private final JBCheckBox showSecondRound;
+ private final JBLabel shownSubmissionsLabel;
+ private final JPanel backlogList;
+
+ private List lastFetchedSubmissions = new ArrayList<>();
+ private final List onBacklogUpdate = new ArrayList<>();
+
+ public BacklogPanel() {
+ super(new MigLayout("wrap 2", "[grow] []"));
+
+ var filterPanel = new JBPanel<>(new MigLayout("wrap 4", "[][grow][][]"));
+ this.add(filterPanel, "spanx 2, growx");
+
+ shownSubmissionsLabel = new JBLabel();
+ filterPanel.add(shownSubmissionsLabel);
+
+ // Disabling history here so that in the exam review the next student can't see the previous student's id
+ searchField = new SearchTextField(false);
+ filterPanel.add(searchField, "growx");
+ searchField.addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(@NotNull DocumentEvent documentEvent) {
+ updateBacklog();
+ }
+ });
+
+ showFirstRound = new JBCheckBox("Round 1");
+ showFirstRound.setSelected(true);
+ showFirstRound.addActionListener(a -> updateBacklog());
+ filterPanel.add(showFirstRound);
+
+ showSecondRound = new JBCheckBox("Round 2");
+ showSecondRound.setSelected(true);
+ showSecondRound.addActionListener(a -> updateBacklog());
+ filterPanel.add(showSecondRound);
+
+ backlogList = new JBPanel<>(new MigLayout("wrap 5, gapx 10", "[][][][][grow]"));
+ this.add(ScrollPaneFactory.createScrollPane(backlogList, true), "spanx 2, growx");
+
+ var refreshButton = new JButton(AllIcons.Actions.Refresh);
+ refreshButton.addActionListener(this::refreshButtonClicked);
+ this.add(refreshButton, "skip 1, alignx right");
+ }
+
+ private void refreshButtonClicked(ActionEvent actionEvent) {
+ for (Runnable runnable : onBacklogUpdate) {
+ runnable.run();
+ }
+ }
+
+ public void addBacklogUpdateListener(Runnable listener) {
+ onBacklogUpdate.add(listener);
+ }
+
+ public void setSubmissions(List submissions) {
+ this.lastFetchedSubmissions = new ArrayList<>(submissions);
+ this.lastFetchedSubmissions.sort(Comparator.comparing(ProgrammingSubmission::getSubmissionDate));
+ this.updateBacklog();
+ }
+
+ public void clear() {
+ this.backlogList.removeAll();
+ this.updateUI();
+ }
+
+ private void updateBacklog() {
+ backlogList.removeAll();
+
+ String searchText = searchField.getText();
+ boolean firstRound = showFirstRound.isSelected();
+ boolean secondRound = showSecondRound.isSelected();
+
+ int shown = 0;
+ for (ProgrammingSubmission submission : lastFetchedSubmissions) {
+ if (searchText != null && !submission.getParticipantIdentifier().contains(searchText)) {
+ continue;
+ }
+
+ if (!firstRound && submission.getCorrectionRound() == 0) {
+ continue;
+ }
+
+ if (!secondRound && submission.getCorrectionRound() == 1) {
+ continue;
+ }
+
+ shown++;
+
+ // Participant
+ backlogList.add(new JBLabel(submission.getParticipantIdentifier()));
+ addSubmissionDateLabel(submission);
+ // Correction Round
+ backlogList.add(new JBLabel("Round " + (submission.getCorrectionRound() + 1)));
+ addScoreItem(submission);
+ addActionButton(submission);
+ }
+
+ shownSubmissionsLabel.setText("Showing %d/%d".formatted(shown, lastFetchedSubmissions.size()));
+
+ this.updateUI();
+ }
+
+ private void addSubmissionDateLabel(ProgrammingSubmission submission) {
+ String dateText = submission
+ .getSubmissionDate()
+ .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT));
+ backlogList.add(new JBLabel(dateText), "alignx right");
+ }
+
+ private void addScoreItem(ProgrammingSubmission submission) {
+ // Score in percent
+ var latestResult = submission.getLatestResult();
+ String resultText = "";
+ if (submission.isSubmitted()) {
+ resultText = latestResult
+ .map(resultDTO -> "%.0f%%".formatted(resultDTO.score()))
+ .orElse("???");
+ }
+ backlogList.add(new JBLabel(resultText), "alignx right");
+ }
+
+ private void addActionButton(ProgrammingSubmission submission) {
+ // Action Button
+ JButton reopenButton;
+ if (submission.isSubmitted()) {
+ reopenButton = new JButton("Reopen");
+ } else if (ArtemisUtils.isSubmissionStarted(submission)) {
+ reopenButton = new JButton("Continue");
+ reopenButton.setForeground(JBColor.ORANGE);
+ } else {
+ reopenButton = new JButton("Start");
+ }
+ reopenButton.addActionListener(a -> PluginState.getInstance().reopenAssessment(submission));
+ backlogList.add(reopenButton, "growx");
+ }
+}
diff --git a/src/main/java/edu/kit/kastel/sdq/intelligrade/extensions/guis/ExercisePanel.java b/src/main/java/edu/kit/kastel/sdq/intelligrade/extensions/guis/ExercisePanel.java
index 5d6fd47..60db200 100644
--- a/src/main/java/edu/kit/kastel/sdq/intelligrade/extensions/guis/ExercisePanel.java
+++ b/src/main/java/edu/kit/kastel/sdq/intelligrade/extensions/guis/ExercisePanel.java
@@ -2,10 +2,6 @@
package edu.kit.kastel.sdq.intelligrade.extensions.guis;
import java.awt.event.ItemEvent;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.ArrayList;
-import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
@@ -14,7 +10,6 @@
import javax.swing.JPanel;
import javax.swing.event.DocumentEvent;
-import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
@@ -35,7 +30,6 @@
import com.intellij.ui.components.TextComponentEmptyText;
import edu.kit.kastel.sdq.artemis4j.ArtemisNetworkException;
import edu.kit.kastel.sdq.artemis4j.client.AssessmentStatsDTO;
-import edu.kit.kastel.sdq.artemis4j.client.AssessmentType;
import edu.kit.kastel.sdq.artemis4j.grading.ArtemisConnection;
import edu.kit.kastel.sdq.artemis4j.grading.Course;
import edu.kit.kastel.sdq.artemis4j.grading.Exam;
@@ -74,8 +68,7 @@ public class ExercisePanel extends SimpleToolWindowPanel {
private JButton closeAssessmentButton;
private JButton reRunAutograder;
- private JPanel backlogPanel;
- private JPanel backlogList;
+ private BacklogPanel backlogPanel;
public ExercisePanel() {
super(true, true);
@@ -108,7 +101,8 @@ public ExercisePanel() {
content.add(assessmentPanel, "span 2, growx");
content.add(new TitledSeparator("Backlog"), "spanx 2, growx");
- createBacklogPanel();
+ backlogPanel = new BacklogPanel();
+ backlogPanel.addBacklogUpdateListener(this::updateBacklogAndStats);
content.add(backlogPanel, "span 2, growx");
setContent(ScrollPaneFactory.createScrollPane(content));
@@ -227,17 +221,6 @@ private void createAssessmentPanel() {
assessmentPanel.add(reRunAutograder, "spanx 2, growx");
}
- private void createBacklogPanel() {
- backlogPanel = new JBPanel<>(new MigLayout("wrap 2", "[grow] []"));
-
- backlogList = new JBPanel<>(new MigLayout("wrap 5, gapx 10", "[][][][][grow]"));
- backlogPanel.add(ScrollPaneFactory.createScrollPane(backlogList, true), "spanx 2, growx");
-
- var refreshButton = new JButton(AllIcons.Actions.Refresh);
- refreshButton.addActionListener(a -> updateBacklogAndStats());
- backlogPanel.add(refreshButton, "skip 1, alignx right");
- }
-
private void handleExerciseSelected(ItemEvent e) {
// Exercise selected: Update plugin state, enable/disable grading buttons, update backlog
if (e.getStateChange() != ItemEvent.DESELECTED) {
@@ -368,15 +351,14 @@ private void updateBacklogAndStats() {
LOG.warn(ex);
ArtemisUtils.displayNetworkErrorBalloon("Failed to fetch backlog or statistics", ex);
ApplicationManager.getApplication().invokeLater(() -> {
- backlogList.removeAll();
- this.updateUI();
+ backlogPanel.clear();
});
return;
}
ApplicationManager.getApplication().invokeLater(() -> {
updateStatistics(exercise, stats, submissions);
- updateBacklog(submissions);
+ backlogPanel.setSubmissions(submissions);
updateUI();
// Tell the user that we've done something
@@ -412,60 +394,11 @@ private void updateStatistics(
int submittedSubmissions = (int)
submissions.stream().filter(ProgrammingSubmission::isSubmitted).count();
int lockedSubmissions = (int)
- submissions.stream().filter(ExercisePanel::isSubmissionStarted).count();
+ submissions.stream().filter(ArtemisUtils::isSubmissionStarted).count();
String userText = "%d (%d locked)".formatted(submittedSubmissions, lockedSubmissions);
userStatisticsLabel.setText(userText);
}
- private void updateBacklog(List submissions) {
- backlogList.removeAll();
-
- List sortedSubmissions = new ArrayList<>(submissions);
- sortedSubmissions.sort(Comparator.comparing(ProgrammingSubmission::getSubmissionDate));
- for (ProgrammingSubmission submission : sortedSubmissions) {
- // Participant
- backlogList.add(new JBLabel(submission.getParticipantIdentifier()));
-
- // Submission date
- String dateText = submission
- .getSubmissionDate()
- .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT));
- backlogList.add(new JBLabel(dateText), "alignx right");
-
- // Correction Round
- backlogList.add(new JBLabel("Round " + (submission.getCorrectionRound() + 1)));
-
- // Score in percent
- var latestResult = submission.getLatestResult();
- String resultText = "";
- if (submission.isSubmitted()) {
- resultText = latestResult
- .map(resultDTO -> "%.0f%%".formatted(resultDTO.score()))
- .orElse("???");
- }
- backlogList.add(new JBLabel(resultText), "alignx right");
-
- // Action Button
- JButton reopenButton;
- if (submission.isSubmitted()) {
- reopenButton = new JButton("Reopen");
- } else if (isSubmissionStarted(submission)) {
- reopenButton = new JButton("Continue");
- reopenButton.setForeground(JBColor.ORANGE);
- } else {
- reopenButton = new JButton("Start");
- }
- reopenButton.addActionListener(a -> PluginState.getInstance().reopenAssessment(submission));
- backlogList.add(reopenButton, "growx");
- }
- }
-
- private static boolean isSubmissionStarted(ProgrammingSubmission submission) {
- return !submission.isSubmitted()
- && submission.getLatestResult().isPresent()
- && submission.getLatestResult().get().assessmentType() != AssessmentType.AUTOMATIC;
- }
-
private record OptionalExam(Exam exam) {
@Override
public String toString() {
diff --git a/src/main/java/edu/kit/kastel/sdq/intelligrade/utils/ArtemisUtils.java b/src/main/java/edu/kit/kastel/sdq/intelligrade/utils/ArtemisUtils.java
index 7c20e20..1307652 100644
--- a/src/main/java/edu/kit/kastel/sdq/intelligrade/utils/ArtemisUtils.java
+++ b/src/main/java/edu/kit/kastel/sdq/intelligrade/utils/ArtemisUtils.java
@@ -7,6 +7,8 @@
import com.intellij.notification.NotificationGroupManager;
import com.intellij.notification.NotificationType;
import edu.kit.kastel.sdq.artemis4j.ArtemisNetworkException;
+import edu.kit.kastel.sdq.artemis4j.client.AssessmentType;
+import edu.kit.kastel.sdq.artemis4j.grading.ProgrammingSubmission;
/**
* Utility Class to handle Artemis related common tasks such as
@@ -24,6 +26,12 @@ public static boolean doesUrlExist(String url) {
}
}
+ public static boolean isSubmissionStarted(ProgrammingSubmission submission) {
+ return !submission.isSubmitted()
+ && submission.getLatestResult().isPresent()
+ && submission.getLatestResult().get().assessmentType() != AssessmentType.AUTOMATIC;
+ }
+
public static void displayGenericErrorBalloon(String title, String content) {
NotificationGroupManager.getInstance()
.getNotificationGroup("IntelliGrade Notifications")