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

I 423, I 765 dynamically update shadow compare runs table, show only missmatched runs #958

Merged
37 changes: 35 additions & 2 deletions src/edu/csus/ecs/pc2/shadow/ShadowController.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,18 @@ public String getLabel() {
return this.label;
}
}

public enum FILTERS {
NONE,
ONLY_MISMATCH
}

private SHADOW_CONTROLLER_STATUS controllerStatus = null ;
private RemoteContestConfiguration remoteContestConfig;
private Thread monitorThread;
private IRemoteContestAPIAdapter remoteContestAPIAdapter;

private boolean convertJudgementsToBig5 = true;

private FILTERS currentFilter = FILTERS.NONE ;
/**
* a ContestInformation Listener
*
Expand Down Expand Up @@ -1028,4 +1032,33 @@ public IInternalContest getLocalContest() {
public IInternalController getLocalController() {
return localController;
}

public FILTERS getFilter() {
return currentFilter;
}

public void setFilter(FILTERS filter) {
currentFilter = filter;
}

/**
*
* @param currentJudgementMap
* @return currentJudgementMap but judgements that shouldnt be shown is removed.
*/
public Map<String, ShadowJudgementInfo> filterJudgmenentMap(Map<String, ShadowJudgementInfo> currentJudgementMap) {
if (getFilter().equals(FILTERS.ONLY_MISMATCH)) {
Map<String, ShadowJudgementInfo> newJudgementMap = new HashMap<String, ShadowJudgementInfo>();
for (String key : currentJudgementMap.keySet()) {

ShadowJudgementPair pair = currentJudgementMap.get(key).getShadowJudgementPair();
if ((! pair.getPc2Judgement().equals(pair.getRemoteCCSJudgement())) &&
! pair.getPc2Judgement().equals("<pending>") && !pair.getRemoteCCSJudgement().equals("<pending>") ){//When one of the judgement is pending it will be filtered out
newJudgementMap.put(key,currentJudgementMap.get(key));
}
}
return newJudgementMap;
}
return currentJudgementMap;
}
}
181 changes: 171 additions & 10 deletions src/edu/csus/ecs/pc2/ui/ShadowCompareRunsPane.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau.
// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau.
package edu.csus.ecs.pc2.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
Expand All @@ -18,18 +21,21 @@
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
Expand All @@ -39,6 +45,10 @@
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;

import edu.csus.ecs.pc2.clics.CLICSJudgementType;
import edu.csus.ecs.pc2.clics.CLICSJudgementType.CLICS_JUDGEMENT_ACRONYM;
Expand All @@ -54,6 +64,7 @@
import edu.csus.ecs.pc2.core.model.RunResultFiles;
import edu.csus.ecs.pc2.core.security.Permission;
import edu.csus.ecs.pc2.shadow.ShadowController;
import edu.csus.ecs.pc2.shadow.ShadowController.FILTERS;
import edu.csus.ecs.pc2.shadow.ShadowJudgementInfo;
import edu.csus.ecs.pc2.shadow.ShadowJudgementPair;

Expand All @@ -69,11 +80,15 @@ public class ShadowCompareRunsPane extends JPanePlugin {

private static final int RUN_UPDATE_REQUEST_SERVER_TIMEOUT_MILLIS = 30000;

private static final String DEFAULT_REFRESH_INTERVAL = "5";

private ShadowController shadowController = null ;

//the current judgement information from the shadow controller
private Map<String, ShadowJudgementInfo> currentJudgementMap = null;

private Map<String, ShadowJudgementInfo> filteredJudgementMap = null;

//the table displaying the current results
private JTable resultsTable = null ;

Expand All @@ -87,6 +102,10 @@ public class ShadowCompareRunsPane extends JPanePlugin {
private Run runWeRequestedServerToUpdate;

private boolean serverHasUpdatedOurRun;

private JPanel dynamicallyRefreshPanel;

private JCheckBox mismatchCheckBox;

@Override
public String getPluginTitle() {
Expand Down Expand Up @@ -141,6 +160,8 @@ public ShadowCompareRunsPane(ShadowController shadowController) {

this.add(getSummaryPanel());

this.add(getdynamicallyRefreshPanel());

this.add(getButtonPanel());
}

Expand Down Expand Up @@ -223,20 +244,22 @@ public boolean isCellEditable(int nRow, int nCol) {
*/
private TableModel getUpdatedResultsTableModel() {

//get the current judgement information from the shadow controller
//get the current judgment information from the shadow controller
currentJudgementMap = shadowController.getJudgementComparisonInfo();


//We don't want summaryPanel to count only mismatches for the summary. Below prevents it.
filteredJudgementMap = shadowController.filterJudgmenentMap(currentJudgementMap);
//define the columns for the table
String[] columnNames = { "Team", "Problem", "Language", "Submission ID", "PC2 Shadow", "Remote CCS", "Match?", "Overridden?" };

//an array to hold the table data
Object[][] data = new Object[currentJudgementMap.size()][8];
Object[][] data = new Object[filteredJudgementMap.size()][8];

//fill in each data row with info from the shadow controller's judgement map
int row = 0;
for (String key : currentJudgementMap.keySet()) {
for (String key : filteredJudgementMap.keySet()) {

ShadowJudgementInfo curJudgementInfo = currentJudgementMap.get(key);
ShadowJudgementInfo curJudgementInfo = filteredJudgementMap.get(key);
data[row][0] = new Integer(Utilities.nullSafeToInt(curJudgementInfo.getTeamID(), 0));
data[row][1] = curJudgementInfo.getProblemID();
data[row][2] = curJudgementInfo.getLanguageID();
Expand Down Expand Up @@ -327,11 +350,150 @@ private ShadowCompareRunsSummaryPane getSummaryPanel() {

}

/**
* Initialized dynamically refresh panel that contains checkbox, textfield and label for auto refreshing.
* @return
*/
private JPanel getdynamicallyRefreshPanel() {

if (dynamicallyRefreshPanel == null) {
dynamicallyRefreshPanel = new JPanel();
dynamicallyRefreshPanel.setMaximumSize(new Dimension(700,20));

JCheckBox checkbox = new JCheckBox("Refresh Every:");
dynamicallyRefreshPanel.add(checkbox);


JTextField textField = new JTextField(DEFAULT_REFRESH_INTERVAL,2);
((AbstractDocument) textField.getDocument()).setDocumentFilter(new DocumentFilter() { //Makes the textfield so that user is not allowed to enter illegal numbers.
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
StringBuilder newText = new StringBuilder(currentText);
newText.replace(offset, offset + length, text);

try {
int value = Integer.parseInt(newText.toString());
if (value >= 1 && value <= 60) {
// Allow values between 1 and 60
super.replace(fb, offset, length, text, attrs);
} else {
JOptionPane.showMessageDialog(null, "Duration of automatic refresh has to be between 1 and 60 seconds", "Warning", JOptionPane.WARNING_MESSAGE);
Toolkit.getDefaultToolkit().beep();
}
} catch (NumberFormatException e) {
//catch if user tries to enter non numeric chars.
Toolkit.getDefaultToolkit().beep();
}

}
});

dynamicallyRefreshPanel.add(textField);

checkbox.addItemListener(new ItemListener() {
Timer timer;
@Override
public void itemStateChanged(ItemEvent e) {


if (e.getStateChange() == ItemEvent.SELECTED) {
if (textField.getText().isEmpty()) {
JOptionPane.showMessageDialog(null, "Please specify the number of seconds between refreshes (1-60)", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
int duration;
try {
duration = Integer.parseInt(textField.getText());
if (duration < 1) {
log.log(Log.WARNING, "dynamicallyRefreshPanel received duration less than 1. It received: "+ textField.getText());
throw new IllegalArgumentException("Duration value cannot be less than 1, time is : " + duration);
}
if (duration > 60) {
log.log(Log.WARNING, "dynamicallyRefreshPanel received duration more than 60. It received: "+ textField.getText());
throw new IllegalArgumentException("Duration value cannot be more than 60, time is: " + duration);
}
} catch (NumberFormatException a) {
log.log(Log.WARNING, "dynamicallyRefreshPanel did not receive an integer for duration. It received: "+ textField.getText());
throw new NumberFormatException("dynamicallyRefreshPanel did not receive an integer for duration. It received: " + textField.getText());
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
refreshResultsTable();
}
});
timer = new Timer(duration * 1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {


SwingUtilities.invokeLater(new Runnable() {
public void run() {
refreshResultsTable();
}
});
}
});
textField.setEnabled(false);
log.log(Log.INFO, "Shadow table will be dynamically refreshed every " + duration + " seconds.");
timer.start();
}
else {
textField.setEnabled(true);
log.log(Log.INFO, " Dynamically refreshing has been stopped.");
timer.stop();
}
}
});

JLabel label = new JLabel("seconds");
dynamicallyRefreshPanel.add(label);

Component horizontalStrut2 = Box.createHorizontalStrut(40);
dynamicallyRefreshPanel.add(horizontalStrut2);


dynamicallyRefreshPanel.add(getmismatchCheckBox());
}
return dynamicallyRefreshPanel;
}

private JCheckBox getmismatchCheckBox() {
if (mismatchCheckBox == null) {
mismatchCheckBox = new JCheckBox("Only Mismatched");
mismatchCheckBox.setToolTipText("When toggled, table will only display when pc2 and remote ccs' judgements do not match");

mismatchCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {

shadowController.setFilter(FILTERS.ONLY_MISMATCH);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
refreshResultsTable();
}
});
}
else {

shadowController.setFilter(FILTERS.NONE);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
refreshResultsTable();
}
});
}
}
});
}
return mismatchCheckBox;
}
private JComponent getButtonPanel() {

JPanel buttonPanel = new JPanel();
buttonPanel.setMaximumSize(new Dimension(500,40));
buttonPanel.setMaximumSize(new Dimension(700,40));


JButton refreshButton = new JButton("Refresh");
refreshButton.addActionListener(new ActionListener() {
Expand Down Expand Up @@ -395,9 +557,8 @@ public void run() {
resolveButton.setToolTipText("Updates PC2 so that the PC2 judgement in all selected table rows matches the Remote CCS judgement");
buttonPanel.add(resolveButton);



return buttonPanel ;

return buttonPanel;
}

private void refreshResultsTable() {
Expand Down
Loading