diff --git a/.gitignore b/.gitignore index fa4cdca6d..e066fc271 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,23 @@ /profiles.properties /profiles/ /testout/ +/html/ +/public_html/ +/results/ +/reports/ +/log/ +passwords.txt +pc2export.dat +results.xml +results_*.xml /.classes /realm.properties /VERSION /pc2ws.properties +cacerts.pc2 + +projects/*.gz +projects/*.zip +projects/WTI-UI/package-lock.json +projects/WTI-UI/.angular/ diff --git a/projects/EWTeam/lib/pc2.jar b/projects/EWTeam/lib/pc2.jar index 881da1b2c..3e0532884 100644 Binary files a/projects/EWTeam/lib/pc2.jar and b/projects/EWTeam/lib/pc2.jar differ diff --git a/projects/WTI-API/WebContent/WEB-INF/lib/pc2.jar b/projects/WTI-API/WebContent/WEB-INF/lib/pc2.jar index c43e5a61f..3e0532884 100644 Binary files a/projects/WTI-API/WebContent/WEB-INF/lib/pc2.jar and b/projects/WTI-API/WebContent/WEB-INF/lib/pc2.jar differ diff --git a/projects/WTI-API/test/controllers/Test_Contest_Get_Clarifications.java b/projects/WTI-API/test/controllers/Test_Contest_Get_Clarifications.java index 2c0475187..dd9326b08 100644 --- a/projects/WTI-API/test/controllers/Test_Contest_Get_Clarifications.java +++ b/projects/WTI-API/test/controllers/Test_Contest_Get_Clarifications.java @@ -19,15 +19,15 @@ /** * Contains various tests to insure proper operation of {@link ContestController} methods which * fetch contest clarifications from the PC2 Contest. - * + * * The setup() prior to running the JUnit tests injects into the ContestController a "mock" PC2 server * connection, so that the test can be run without actually having a PC2 server/contest running. - * - * Each test method is responsible for configuring the "mock contest" values located as protected fields in - * in the {@link ContestControllerInjection} superclass (and therefore accessible by this class). - * (This is a somewhat poor use of inheritance, but it does provide a convenient way give all JUnit tests + * + * Each test method is responsible for configuring the "mock contest" values located as protected fields in + * in the {@link ContestControllerInjection} superclass (and therefore accessible by this class). + * (This is a somewhat poor use of inheritance, but it does provide a convenient way give all JUnit tests * access to the mock PC2 server connection.) - * + * * @author EWU WebTeamClient project team, with updates by John Clevenger, PC2 Development Team (pc2.ecs.csus.edu) * */ @@ -36,7 +36,7 @@ public class Test_Contest_Get_Clarifications extends ContestControllerInjection{ private Response response; private ContestController controller; - + //a (mock) clarification private String teamName = "Team1"; private String problem = "A. Dogzilla"; @@ -45,7 +45,7 @@ public class Test_Contest_Get_Clarifications extends ContestControllerInjection{ @Before public void setUp() throws Exception { - + //inject a Mock PC2 server connection into the ContestController this.contestControllerInjection(); this.controller = new ContestController(); @@ -62,20 +62,20 @@ public void Test_Get_Clarifications_401_Unauthorized_User(){ this.response = this.controller.clarifications("inValidId"); assertEquals(401, this.response.getStatus()); } - + /** * Test that under normal circumstances (logged in, contest properly configured and running), requesting clarifications * returns "200" (OK). - * + * * @throws Exception */ @Test - public void Test_Get_Clarifications_200() throws Exception{ - + public void Test_Get_Clarifications_200() throws Exception{ + Mockito.when(connection.getContest().getClarifications()).thenReturn(new IClarification[] {mockedClarification}); Mockito.when(mockedClarification.getTeam()).thenReturn(mockedTeam); - Mockito.when(mockedClarification.getTeam().getGroup()).thenReturn(mockedGroup); - Mockito.when(mockedClarification.getTeam().getGroup().getName()).thenReturn(teamName); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup()).thenReturn(mockedGroup); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup().getName()).thenReturn(teamName); Mockito.when(mockedClarification.getTeam().getDisplayName()).thenReturn(teamName.toLowerCase()); Mockito.when(mockedClarification.getTeam().getLoginName()).thenReturn(teamName.toLowerCase()); Mockito.when(mockedClarification.getProblem()).thenReturn(mockedProblem); @@ -85,44 +85,44 @@ public void Test_Get_Clarifications_200() throws Exception{ Mockito.when(mockedClarification.getSubmissionTime()).thenReturn(12345678L); Mockito.when(mockedClarification.isAnswered()).thenReturn(true); Mockito.when(connection.getMyClient().getLoginName()).thenReturn("Team1"); - + //make sure the mock says that the contest is running for this test Mockito.when(connection.getContest().isContestClockRunning()).thenReturn(true); this.response = this.controller.clarifications(testKey); assertEquals(200, this.response.getStatus()); } - + /** - * Test that, while properly logged in and the contest is running, when requesting a list of the clarifications submitted by a team - * but there is a clarification that has no problem associated with it, + * Test that, while properly logged in and the contest is running, when requesting a list of the clarifications submitted by a team + * but there is a clarification that has no problem associated with it, * "500" (INTERNAL_SERVER_ERROR) is returned -- because the server should never have accepted a * clarification that doesn't have a valid problem associated with it. * @throws Exception */ @SuppressWarnings("unchecked") @Test - public void Test_Get_Clarifications_500_Clar_Has_No_Associated_Problem() throws Exception { - + public void Test_Get_Clarifications_500_Clar_Has_No_Associated_Problem() throws Exception { + Mockito.when(connection.getContest().getClarifications()).thenReturn(new IClarification[] {mockedClarification}); - + Mockito.when(mockedClarification.getTeam()).thenReturn(mockedTeam); - Mockito.when(mockedClarification.getTeam().getGroup()).thenReturn(mockedGroup); - Mockito.when(mockedClarification.getTeam().getGroup().getName()).thenReturn(teamName.toLowerCase()); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup()).thenReturn(mockedGroup); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup().getName()).thenReturn(teamName.toLowerCase()); Mockito.when(mockedClarification.getTeam().getDisplayName()).thenReturn(teamName.toLowerCase()); Mockito.when(mockedClarification.getTeam().getLoginName()).thenReturn(teamName.toLowerCase()); Mockito.when(mockedClarification.getProblem()).thenThrow(NullPointerException.class); Mockito.when(connection.getMyClient().getLoginName()).thenReturn(teamName); Mockito.when(connection.getContest().getProblems()).thenThrow(NullPointerException.class); - + //make sure the mock says that the contest is running for this test Mockito.when(connection.getContest().isContestClockRunning()).thenReturn(true); this.response = this.controller.clarifications(testKey); assertEquals(500, this.response.getStatus()); } - + /** * Test that an attempt to fetch clarifications when properly logged in but the contest is not running returns * "401" (Unauthorized). @@ -131,33 +131,33 @@ public void Test_Get_Clarifications_500_Clar_Has_No_Associated_Problem() throws @SuppressWarnings("unchecked") @Test public void Test_Get_Clarifications_401_Contest_Not_Running() throws Exception{ - + //define some (mock) clarifications, even though we shouldn't be able to access them Mockito.when(connection.getContest().getClarifications()).thenReturn(new IClarification[] {mockedClarification}); - + Mockito.when(mockedClarification.getTeam()).thenReturn(mockedTeam); - Mockito.when(mockedClarification.getTeam().getGroup()).thenReturn(mockedGroup); - Mockito.when(mockedClarification.getTeam().getGroup().getName()).thenReturn("Team1"); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup()).thenReturn(mockedGroup); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup().getName()).thenReturn("Team1"); Mockito.when(mockedClarification.getTeam().getDisplayName()).thenReturn(teamName.toLowerCase()); Mockito.when(mockedClarification.getTeam().getLoginName()).thenReturn(teamName.toLowerCase()); - + Mockito.when(connection.getMyClient().getLoginName()).thenReturn(teamName); Mockito.when(connection.getContest().getProblems()).thenThrow(NotLoggedInException.class); - - //make sure the mock says that the contest is not running for this test + + //make sure the mock says that the contest is not running for this test Mockito.when(connection.getContest().isContestClockRunning()).thenReturn(false); - + this.response = this.controller.clarifications(testKey); assertEquals(401, this.response.getStatus()); } - + /** - * Test that requesting the first clarification from Site 1 returns a clarification with is "1-1" + * Test that requesting the first clarification from Site 1 returns a clarification with is "1-1" * and a response code of "200" (OK). * @throws Exception */ @Test public void Test_Get_Clarifications_200_Id_Received() throws Exception{ - + //return Mocked Clarification info when the server (via the userconnection) asks the contest for clarifications Mockito.when(connection.getContest().getClarifications()).thenReturn(new IClarification[] {mockedClarification}); Mockito.when(mockedClarification.getQuestion()).thenReturn(question); @@ -166,36 +166,36 @@ public void Test_Get_Clarifications_401_Contest_Not_Running() throws Exception{ Mockito.when(mockedClarification.isAnswered()).thenReturn(true); Mockito.when(mockedClarification.getSiteNumber()).thenReturn(1); Mockito.when(mockedClarification.getNumber()).thenReturn(1); - - //return Mocked Team info when the server (via the userconnection) asks the Clarification for Team info + + //return Mocked Team info when the server (via the userconnection) asks the Clarification for Team info Mockito.when(mockedClarification.getTeam()).thenReturn(mockedTeam); - Mockito.when(mockedClarification.getTeam().getGroup()).thenReturn(mockedGroup); - Mockito.when(mockedClarification.getTeam().getGroup().getName()).thenReturn(teamName); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup()).thenReturn(mockedGroup); + Mockito.when(mockedClarification.getTeam().getPrimaryGroup().getName()).thenReturn(teamName); Mockito.when(mockedClarification.getTeam().getDisplayName()).thenReturn(teamName.toLowerCase()); Mockito.when(mockedClarification.getTeam().getLoginName()).thenReturn(teamName.toLowerCase()); - + //return Mocked Problem info when the server (via the userconnection) asks the Clarification for Problem info Mockito.when(mockedClarification.getProblem()).thenReturn(mockedProblem); Mockito.when(mockedClarification.getProblem().getName()).thenReturn(problem); - + Mockito.when(connection.getMyClient().getLoginName()).thenReturn(teamName); - //make sure the mock says that the contest is running for this test + //make sure the mock says that the contest is running for this test Mockito.when(connection.getContest().isContestClockRunning()).thenReturn(true); - + this.response = this.controller.clarifications(testKey); assertEquals(200, this.response.getStatus()); @SuppressWarnings("unchecked") List clars = (List) this.response.getEntity(); assertEquals("1-1", clars.get(0).id); - + } - + @After public void tearDown() throws Exception { if(response != null) this.response.close(); } - + } diff --git a/src/edu/csus/ecs/pc2/api/ITeam.java b/src/edu/csus/ecs/pc2/api/ITeam.java index 886768bc4..453d491b6 100644 --- a/src/edu/csus/ecs/pc2/api/ITeam.java +++ b/src/edu/csus/ecs/pc2/api/ITeam.java @@ -1,15 +1,19 @@ // Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.api; +import java.util.HashMap; + +import edu.csus.ecs.pc2.core.model.ElementId; + /** * This interface describes the PC2 API view of a contest Team. * Note that every Team in the API view is a subclass of {@link IClient}, * so a "team view" also contains the general information described by * {@link IClient}. - * + * *

* This documentation describes the current draft of the PC2 API, which is subject to change. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -19,9 +23,16 @@ public interface ITeam extends IClient { /** * Get the group associate with this team. - * + * * @see IGroup * @return group information. */ - IGroup getGroup(); + public HashMap getGroups(); + + /** + * Get the primary (cms) group for a team + * + * @return id of the CMS supplied group (so-called primary group) + */ + public IGroup getPrimaryGroup(); } diff --git a/src/edu/csus/ecs/pc2/api/implementation/TeamImplementation.java b/src/edu/csus/ecs/pc2/api/implementation/TeamImplementation.java index 7762167fc..1afde6bac 100644 --- a/src/edu/csus/ecs/pc2/api/implementation/TeamImplementation.java +++ b/src/edu/csus/ecs/pc2/api/implementation/TeamImplementation.java @@ -1,15 +1,18 @@ // Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.api.implementation; +import java.util.HashMap; + import edu.csus.ecs.pc2.api.IGroup; import edu.csus.ecs.pc2.api.ITeam; import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.IInternalContest; /** * Implementation for ITeam. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -21,7 +24,8 @@ public class TeamImplementation extends ClientImplementation implements ITeam { private String shortName; - private IGroup group = null; + private HashMap groups = null; + private IGroup primaryGroup = null; public TeamImplementation(ClientId submitter, IInternalContest internalContest) { super(submitter, internalContest); @@ -38,12 +42,21 @@ public TeamImplementation(ClientId submitter, IInternalContest internalContest) private void setAccountValues(Account account, IInternalContest contest) { displayName = account.getDisplayName(); shortName = account.getClientId().getName(); - if (account.getGroupId() != null) { - // SOMEDAY ensure that groupId is not null - // this happened on load of teams.tsv and groups.tsv - if (contest.getGroup(account.getGroupId()) != null){ - group = new GroupImplementation(account.getGroupId(), contest); + if (account.getGroupIds() != null) { + for(ElementId elementId: account.getGroupIds()) { + if (contest.getGroup(elementId) != null){ + if(groups == null) { + groups = new HashMap(); + } + GroupImplementation group = new GroupImplementation(elementId, contest); + groups.put(elementId, group); + if(primaryGroup == null && account.getPrimaryGroupId() == elementId) { + primaryGroup = group; + } + } } + } else { + groups = null; } } @@ -52,16 +65,24 @@ public TeamImplementation(Account account, IInternalContest contest) { setAccountValues(account, contest); } + @Override public String getDisplayName() { return displayName; } - public IGroup getGroup() { - return group; + @Override + public HashMap getGroups() { + return groups; } + @Override public String getLoginName() { return shortName; } + @Override + public IGroup getPrimaryGroup() { + return primaryGroup; + } + } diff --git a/src/edu/csus/ecs/pc2/api/reports/APIReports.java b/src/edu/csus/ecs/pc2/api/reports/APIReports.java index 348f4e4c9..4212fa63e 100644 --- a/src/edu/csus/ecs/pc2/api/reports/APIReports.java +++ b/src/edu/csus/ecs/pc2/api/reports/APIReports.java @@ -1,6 +1,8 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.api.reports; import java.util.Arrays; +import java.util.HashMap; import edu.csus.ecs.pc2.VersionInfo; import edu.csus.ecs.pc2.api.BaseClient; @@ -12,10 +14,11 @@ import edu.csus.ecs.pc2.api.exceptions.LoginFailureException; import edu.csus.ecs.pc2.api.exceptions.NotLoggedInException; import edu.csus.ecs.pc2.core.model.ContestTime; +import edu.csus.ecs.pc2.core.model.ElementId; /** * Print API Info. - * + * * @author Douglas A. Lane */ public class APIReports extends BaseClient { @@ -34,16 +37,16 @@ protected int toInt(String string, int defaultNumber) { return defaultNumber; } - + public void println(String s) { System.out.println(s); } - + public void print(String s) { System.out.print(s); } - + private void printRuns() { if (getContest().getRuns().length == 0) { @@ -80,35 +83,35 @@ private void println() { } public void printDefaultInfo() { - + println("Contest title: '" + getContest().getContestTitle() + "'"); println("Site Name = " + getContest().getSiteName()); - + printClockInfo(); - + println("Contacted: host=" + getContest().getServerHostName() + " port=" + getContest().getServerPort()); - + println(); - + printRunSummary(); - + println(); - + printRuns(); - + println(); - + // printClarSummary(); - + // printTeamSummary(); // new PrintTeams(), // - + printTeamInfo(); - + println(); } - + private void printRunSummary() { print("Run summary: "); @@ -153,21 +156,33 @@ private void printClockInfo() { print(" remaining=" + clock.getRemainingSecs() + " (" + ContestTime.formatTime(clock.getRemainingSecs()) + ")"); print(" elapsed=" + clock.getElapsedSecs() + " (" + ContestTime.formatTime(clock.getElapsedSecs()) + ")"); println(); - - + + } private void printTeamInfo() { println("There are " + getContest().getTeams().length + " team "); for (ITeam team : getContest().getTeams()) { - IGroup group = team.getGroup(); - String name = "(no group assigned)"; - if (group != null) { - name = group.getName(); + HashMap groups = team.getGroups(); + String name = ""; + boolean first = true; + for(ElementId groupElementId : groups.keySet()) { + IGroup group = groups.get(groupElementId); + if(group != null) { + if(first) { + first = false; + } else { + name = name + ","; + } + name = name + group.getName(); + } + } + if(name.isEmpty()) { + name = "(no groups assigned)"; } - println(team.getLoginName() + " title: " + team.getLoginName() + " group: " + name); + println(team.getLoginName() + " title: " + team.getLoginName() + " groups: " + name); } - + } @Override @@ -232,24 +247,24 @@ void printReportsList() { @Override public void printProgramUsageInformation() { - String[] lines = { // + String[] lines = { // "Usage: APIReports [--help] [-F optionsfile] [--all] --login user [--password pass] reportLIst ", // - "", // - "Purpose: Print contest reports/information", // - "", // - "Ex. APIReports --login a3 ", // - "", // + "", // + "Purpose: Print contest reports/information", // + "", // + "Ex. APIReports --login a3 ", // + "", // "--help this message", // - "--all print all reports", - "", // - "reportList - list of report numbers, see below. ", // - "", // + "--all print all reports", + "", // + "reportList - list of report numbers, see below. ", // + "", // }; for (String s : lines) { System.out.println(s); } - + System.out.println("## Title"); printReportsList(); @@ -258,7 +273,7 @@ public void printProgramUsageInformation() { } public static void main(String[] args) { - + APIReports reports = new APIReports(); try { reports.login(args); diff --git a/src/edu/csus/ecs/pc2/api/reports/PrintTeams.java b/src/edu/csus/ecs/pc2/api/reports/PrintTeams.java index c51ff6ab5..7e358b517 100644 --- a/src/edu/csus/ecs/pc2/api/reports/PrintTeams.java +++ b/src/edu/csus/ecs/pc2/api/reports/PrintTeams.java @@ -1,12 +1,15 @@ -// 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.api.reports; +import java.util.HashMap; + import edu.csus.ecs.pc2.api.IGroup; import edu.csus.ecs.pc2.api.ITeam; +import edu.csus.ecs.pc2.core.model.ElementId; /** * Teams. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -16,12 +19,24 @@ public class PrintTeams extends APIAbstractTest { public void printTest() { println("There are " + getContest().getTeams().length + " team "); for (ITeam team : getContest().getTeams()) { - IGroup group = team.getGroup(); - String name = "(no group assigned)"; - if (group != null) { - name = group.getName(); + HashMap groups = team.getGroups(); + String name = ""; + boolean first = true; + for(ElementId groupElementId : groups.keySet()) { + IGroup group = groups.get(groupElementId); + if(group != null) { + if(first) { + first = false; + } else { + name = name + ","; + } + name = name + group.getName(); + } + } + if(name.isEmpty()) { + name = "(no groups assigned)"; } - println(team.getLoginName() + " title: " + team.getLoginName() + " group: " + name); + println(team.getLoginName() + " title: " + team.getLoginName() + " groups: " + name); } println(""); println(); diff --git a/src/edu/csus/ecs/pc2/core/PacketHandler.java b/src/edu/csus/ecs/pc2/core/PacketHandler.java index 68026cf2d..4fe1b8840 100644 --- a/src/edu/csus/ecs/pc2/core/PacketHandler.java +++ b/src/edu/csus/ecs/pc2/core/PacketHandler.java @@ -1,4 +1,4 @@ -// 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.core; import java.io.File; @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashSet; import java.util.Vector; import java.util.logging.Level; @@ -70,9 +71,9 @@ /** * Process all incoming packets. - * + * * Process packets. In {@link #handlePacket(Packet, ConnectionHandlerID) handlePacket} a packet is unpacked, contest is updated, and controller used to send packets as needed. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -83,7 +84,7 @@ public class PacketHandler { private IInternalContest contest = null; private IInternalController controller = null; - + /** * Message handler for conditions where attention may be needed. */ @@ -104,28 +105,28 @@ public PacketHandler(InternalController controller, IInternalContest contest) { /** * Take each input packet, update the contest, send out packets as needed. - * + * * @param packet * @param connectionHandlerID - * @throws Exception + * @throws Exception */ public void handlePacket(Packet packet, ConnectionHandlerID connectionHandlerID) throws Exception { Type packetType = packet.getType(); - + info("handlePacket start " + packet); PacketFactory.dumpPacket(controller.getLog(), packet, "handlePacket"); if (Utilities.isDebugMode()) { PacketFactory.dumpPacket(System.out, packet, "handlePacket"); } - + if (forwardedToProxy(packet)){ - + // Handled proxy packets (send/receive), no additional action necesary in this method. return; } - - + + ClientId fromId = packet.getSourceId(); Clarification clarification; @@ -143,7 +144,7 @@ public void handlePacket(Packet packet, ConnectionHandlerID connectionHandlerID) runSubmission(packet, fromId, connectionHandlerID); break; case RUN_SUBMISSION_CONFIRM_SERVER: - // RUN send from one server to another + // RUN send from one server to another handleRunSubmissionConfirmationServer (packet, fromId); break; case CLARIFICATION_SUBMISSION: @@ -259,28 +260,28 @@ public void handlePacket(Packet packet, ConnectionHandlerID connectionHandlerID) // InternalContest Clock stopped sent from server to clients clockStopped(packet); break; - + default: /** * To avoid the method too many lines limit, the switch * for packetType is handled by this method. */ handleOtherPacketTypes(packetType, fromId, packet, connectionHandlerID); - } - } + } + } /** * Handle proxy packets. - * + * * Will forward packet from a proxy or will send a packet to a proxy. - * + * * @param packet * @return true if packet was forwarded. */ public boolean forwardedToProxy(Packet packet) { // for a server that is not us, and we are a server - + if (contest != null && isServer() && !isThisSite(packet.getDestinationId())) { int siteNumber = packet.getDestinationId().getSiteNumber(); Site destSite = contest.getSite(siteNumber); @@ -304,13 +305,13 @@ public boolean forwardedToProxy(Packet packet) { /** * Handle packets that are not handled by {@link #handlePacket(Packet, ConnectionHandlerID)}. - * - * + * + * * @param packetType * @param fromId * @param packet * @param connectionHandlerID - * @throws Exception + * @throws Exception */ private void handleOtherPacketTypes(Type packetType, ClientId fromId, Packet packet, ConnectionHandlerID connectionHandlerID) throws Exception { @@ -424,15 +425,15 @@ private void handleOtherPacketTypes(Type packetType, ClientId fromId, Packet pac case REQUEST_SERVER_STATUS: handleRequestServerStatus (packet, connectionHandlerID); break; - + case SERVER_STATUS: handleServerStatus(packet, connectionHandlerID); break; - - case SYNCHRONIZE_REMOTE_DATA: + + case SYNCHRONIZE_REMOTE_DATA: handleSynchronizeRemoteData(packet, connectionHandlerID); break; - + case REQUEST_REMOTE_DATA: // Update remote data from other server. handleRequestRemoteData(packet, connectionHandlerID); @@ -441,27 +442,27 @@ private void handleOtherPacketTypes(Type packetType, ClientId fromId, Packet pac case UPDATE_REMOTE_DATA: loginSuccess(packet, connectionHandlerID, fromId); break; - + case SHUTDOWN: handleServerShutdown (packet, connectionHandlerID, fromId); break; - + case SHUTDOWN_ALL: handleShutdownAllServers (packet, connectionHandlerID, fromId); break; - + case START_PLAYBACK: handleStartPlayback (packet, connectionHandlerID, fromId); break; - + case STOP_PLAYBACK: handleStopPlayback (packet, connectionHandlerID, fromId); break; - + case AUTO_REGISTRATION_SUCCESS: handleAutoRegistratioSuccess(packet, connectionHandlerID); break; - + default: Exception exception = new Exception("PacketHandler.handlePacket Unhandled packet " + packet); controller.getLog().log(Log.WARNING, "Unhandled Packet ", exception); @@ -475,23 +476,23 @@ private void handleAutoRegistratioSuccess(Packet packet, ConnectionHandlerID con Account account = (Account) PacketFactory.getObjectValue(packet, PacketFactory.ACCOUNT); String message = account.getDisplayName() + PacketType.FIELD_DELIMIT + account.getClientId().getName() + PacketType.FIELD_DELIMIT + account.getPassword(); - + contest.addMessage(Area.AUTOREG, packet.getSourceId(), packet.getDestinationId(), message); } private void handleStopPlayback(Packet packet, ConnectionHandlerID connectionHandlerID, ClientId fromId) throws Exception { // SOMEDAY 673 forward this start playback to other servers - + securityCheck(Permission.Type.STOP_PLAYBACK, fromId, connectionHandlerID); - + PlaybackInfo playbackInfo = (PlaybackInfo) PacketFactory.getObjectValue(packet, PacketFactory.PLAYBACK_INFO); contest.stopReplayPlaybackInfo(playbackInfo); } private void handleStartPlayback(Packet packet, ConnectionHandlerID connectionHandlerID, ClientId fromId) throws Exception { - + PlaybackInfo playbackInfo = (PlaybackInfo) PacketFactory.getObjectValue(packet, PacketFactory.PLAYBACK_INFO); - + if ( isServer()) { if (playbackInfo.isStarted()) { @@ -499,25 +500,26 @@ private void handleStartPlayback(Packet packet, ConnectionHandlerID connectionHa } else { securityCheck(Permission.Type.STOP_PLAYBACK, fromId, connectionHandlerID); } - + PlaybackManager manager = contest.getPlaybackManager(); - + PlaybackInfo currentPlaybackInfo = manager.getPlaybackInfo(); - + if (currentPlaybackInfo.getReplayList().length == 0) { currentPlaybackInfo = manager.createPlaybackInfo(playbackInfo.getFilename(), contest); } - + currentPlaybackInfo.setWaitBetweenEventsMS(playbackInfo.getWaitBetweenEventsMS()); currentPlaybackInfo.setMinimumPlaybackRecords(playbackInfo.getMinimumPlaybackRecords()); contest.getPlaybackManager().insureMinimumPlaybackRecords(currentPlaybackInfo.getMinimumPlaybackRecords()); if (! manager.isPlaybackRunning() && playbackInfo.isStarted()) { - + // start if NOT running. manager.startPlayback(contest, controller, new Runnable() { + @Override public void run() { PlaybackManager manager = contest.getPlaybackManager(); PlaybackRecord record = manager.getCurrentPlaybackRecord(); @@ -528,10 +530,10 @@ public void run() { } }); } - + if (manager.isPlaybackRunning() && (! playbackInfo.isStarted())){ // stop if running. - + manager.setPlaybackRunning(false); currentPlaybackInfo.setStarted(false); } @@ -550,17 +552,17 @@ public void run() { controller.sendToServers(updatePacket); } else { - + contest.updatePlaybackInfo(playbackInfo); } } /** * Send a packet to all servers to request remote data. - * + * * This will start the process for a server to synchronize - * its remote data by sending a request to all servers. - * + * its remote data by sending a request to all servers. + * * @param packet * @param connectionHandlerID */ @@ -570,12 +572,12 @@ private void handleSynchronizeRemoteData(Packet packet, ConnectionHandlerID conn } /** - * + * * @param packet * @param connectionHandlerID */ private void handleRequestRemoteData(Packet packet, ConnectionHandlerID connectionHandlerID) { - + int targetSiteId = packet.getSourceId().getSiteNumber(); info("Start send remote data to site "+targetSiteId); @@ -589,9 +591,9 @@ private void handleRequestRemoteData(Packet packet, ConnectionHandlerID connecti private void handleRunSubmissionConfirmationServer(Packet packet, ClientId fromId) throws IOException, ClassNotFoundException, FileSecurityException { Run run = (Run) PacketFactory.getObjectValue(packet, PacketFactory.RUN); RunFiles runFiles = (RunFiles) PacketFactory.getObjectValue(packet, PacketFactory.RUN_FILES); - + contest.addRun(run, runFiles); - + if (isServer()) { Packet confirmPacket = PacketFactory.createRunSubmissionConfirm(contest.getClientId(), fromId, run); controller.sendToJudgesAndOthers(confirmPacket, false); @@ -602,18 +604,18 @@ private void handleRunSubmissionConfirmationServer(Packet packet, ClientId fromI controller.logWarning("Unexpected packet on client", ex); } } - + } /** * Handle switch packet. - * + * * @param packet * @param connectionHandlerID - * @throws ProfileException - * @throws ClassNotFoundException - * @throws IOException - * @throws FileSecurityException + * @throws ProfileException + * @throws ClassNotFoundException + * @throws IOException + * @throws FileSecurityException */ private void handleSwitchProfile(Packet packet, ConnectionHandlerID connectionHandlerID) throws ProfileException, IOException, ClassNotFoundException, FileSecurityException { @@ -622,7 +624,7 @@ private void handleSwitchProfile(Packet packet, ConnectionHandlerID connectionHa Profile newProfile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.NEW_PROFILE); String contestPassword = (String) PacketFactory.getObjectValue(packet, PacketFactory.CONTEST_PASSWORD); - + if (newProfile.getSiteNumber() == 0){ newProfile.setSiteNumber(contest.getSiteNumber()); } @@ -631,11 +633,11 @@ private void handleSwitchProfile(Packet packet, ConnectionHandlerID connectionHa // Use existing contest password if no contest password specified. contestPassword = contest.getContestPassword(); } - + if (!new File(newProfile.getProfilePath()).isDirectory()) { - + throw new ProfileException("Unable to switch - can not find profile on disk"); - + // FIXME code up Profile does not exist on this server, so must try to switch on originating server. // /** // * Profile does not exist on this server, so must try to switch on originating server. @@ -647,15 +649,15 @@ private void handleSwitchProfile(Packet packet, ConnectionHandlerID connectionHa // return; // } } - + ProfileManager manager = new ProfileManager(); if (manager.isProfileAvailable(newProfile, contest.getSiteNumber(), contestPassword.toCharArray())) { -// PacketFactory.dumpPacket(System.out, packet, "handleSwitchProfile switchProfile"); // debug +// PacketFactory.dumpPacket(System.out, packet, "handleSwitchProfile switchProfile"); // debug IInternalContest newContest = switchProfile(contest, newProfile, contestPassword.toCharArray(), true); contest = newContest; - + sendStatusToServers (packet, newProfile); } else { @@ -665,66 +667,66 @@ private void handleSwitchProfile(Packet packet, ConnectionHandlerID connectionHa /** * Switch to new profile. - * + * * Load new profile from disk, replace instances of old contest with new contests including all data. - * - * @param currentContest contest switch from + * + * @param currentContest contest switch from * @param newProfile profile to switch to * @param contestPassword contest password for newProfile * @param sendToOtherServers true if this is the first server switching profiles, false if not - * @param packet + * @param packet * @return new contest based on newProfile * @throws ProfileException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ protected IInternalContest switchProfile(IInternalContest currentContest, Profile newProfile, char[] contestPassword, boolean sendToOtherServers) throws ProfileException, IOException, ClassNotFoundException, FileSecurityException { return switchProfile( currentContest, newProfile, contestPassword, sendToOtherServers, null, sendToOtherServers); } - + /** - * Switch profile on this server then send out switch to all local clients. - * + * Switch profile on this server then send out switch to all local clients. + * * @param currentContest * @param newProfile * @param contestPassword * @param sendToOtherServers * @param packet - * @param sendToServers + * @param sendToServers * @return * @throws ProfileException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ protected IInternalContest switchProfile(IInternalContest currentContest, Profile newProfile, char[] contestPassword, boolean sendToOtherServers, Packet packet, boolean sendToServers) throws ProfileException, IOException, ClassNotFoundException, FileSecurityException { - + if (contest.getProfile().getName().equals(newProfile.getName())){ PacketFactory.dumpPacket(System.out, packet, "switch packet, tried to switch to same profile"); throw new ProfileException("Attempted to switch to the same profile: "+newProfile.getName()); } - + ProfileManager manager = new ProfileManager(); - + IInternalContest newContest = new InternalContest(); info("Start switching to profile "+contest.getProfile().getName()+" contest id = "+contest.getContestIdentifier()); - + newContest.setClientId(contest.getClientId()); if (newContest.getSiteNumber() == 0){ newContest.setSiteNumber(contest.getSiteNumber()); } - + info("switchProfile start - to "+newProfile+" as Site "+contest.getSiteNumber()+" as user "+newContest.getClientId()); - + IStorage storage = manager.getProfileStorage(newProfile, contest.getSiteNumber(), contestPassword); newContest.setStorage(storage); - + info("switchProfile save config to "+storage.getDirectoryName()); - + newContest.setContestPassword(new String(contestPassword)); try { @@ -735,11 +737,11 @@ protected IInternalContest switchProfile(IInternalContest currentContest, Profil } catch (Exception e) { throw new ProfileException(newProfile, "Unable to read configuration ", e); } - + if (newContest.getProfile() == null){ newContest.setProfile(newProfile); } - + try { newContest.storeConfiguration(controller.getLog()); } catch (Exception e) { @@ -756,7 +758,7 @@ protected IInternalContest switchProfile(IInternalContest currentContest, Profil } catch (CloneException e) { info(e); } - + /* * Set new contest and add all listeners. */ @@ -768,12 +770,12 @@ protected IInternalContest switchProfile(IInternalContest currentContest, Profil /** * Load configuration information from remoteServer */ - + updateSitesToModel(packet); - + // This will load information and also re-log us into other servers - // to get the most up to date information from those servers. - + // to get the most up to date information from those servers. + ContestLoader loader = new ContestLoader(); loadSettingsFromRemoteServer (loader, packet, null); @@ -782,19 +784,19 @@ protected IInternalContest switchProfile(IInternalContest currentContest, Profil } contest.fireAllRefreshEvents(); - + storeProfiles(); - + // newProfile might not have the correct elementId, so use contest.getProfile() to get the real one. sendOutChangeProfileToAllClients(contest, currentContest.getProfile(), contest.getProfile(), new String(contestPassword), sendToServers); - + info("switchProfile done - to "+contest.getProfile()+" as Site "+contest.getSiteNumber()+" as user "+newContest.getClientId()); info("Switched to profile "+contest.getProfile().getName()+" contest id = "+contest.getContestIdentifier()); if ((! controller.isUsingGUI()) || Utilities.isDebugMode()){ System.out.println(new Date() + " Switched to profile "+contest.getProfile().getName()+" contest id = "+contest.getContestIdentifier()); } - + Profile profile = contest.getProfile(); if (! controller.isUsingGUI()){ System.out.println(new Date()+" Switch to Profile: "+profile.getName()+" @ "+profile.getProfilePath()); @@ -807,18 +809,18 @@ protected IInternalContest switchProfile(IInternalContest currentContest, Profil /** * Create and send current profile to all clients and servers. - * + * * @param newContest * @param currentProfile * @param switchToProfile * @param contestPassword used to encrypt/decrypt config files. - * @param b + * @param b */ protected void sendOutChangeProfileToAllClients(IInternalContest newContest, Profile currentProfile, Profile switchToProfile, String contestPassword, boolean sendToServers) { ContestLoginSuccessData data ; Packet packet ; - + if (sendToServers){ data = createContestLoginSuccessData(newContest, getServerClientId(), contestPassword); packet = PacketFactory.createUpdateProfileClientPacket(getServerClientId(), PacketFactory.ALL_SERVERS, currentProfile, switchToProfile, data); @@ -850,7 +852,7 @@ protected void sendOutChangeProfileToAllClients(IInternalContest newContest, Pro ClientId[] teams = contest.getLocalLoggedInClients(ClientType.Type.TEAM); for (ClientId clientId : teams) { - + if (newContest.getAccount(clientId) != null) { // Account exists in new profile/config @@ -869,12 +871,12 @@ protected void sendOutChangeProfileToAllClients(IInternalContest newContest, Pro /** * Send cloned packet to user class (ClientType). - * + * * Will loop through all logged in users for ClientType and send packet to users. - * + * * To send to only accounts/user logins that exist in the new profile/contest configuration, * set the confirmUserExists to true. - * + * * @param packet packet to send * @param type class of user to send to. * @param newContest data used to confirm that account/clientId exists. @@ -882,9 +884,9 @@ protected void sendOutChangeProfileToAllClients(IInternalContest newContest, Pro */ private void sendClonePacketToUsers(Packet packet, edu.csus.ecs.pc2.core.model.ClientType.Type type, IInternalContest newContest, boolean confirmUserExists) { ClientId[] users = contest.getLocalLoggedInClients(type); - + for (ClientId clientId : users) { - + try { if (!confirmUserExists) { // send unconditional to client @@ -905,30 +907,30 @@ private void sendClonePacketToUsers(Packet packet, edu.csus.ecs.pc2.core.model.C } /** - * Update Client Profile handle new contest profile/settings. - * + * Update Client Profile handle new contest profile/settings. + * * @param packet * @param connectionHandlerID * @throws IOException * @throws ClassNotFoundException * @throws FileSecurityException - * @throws ProfileException - * @throws ProfileCloneException + * @throws ProfileException + * @throws ProfileCloneException */ private void handleUpdateClientProfile(Packet packet, ConnectionHandlerID connectionHandlerID) throws IOException, ClassNotFoundException, FileSecurityException, ProfileException, ProfileCloneException { if (isServer()) { - + /** - * Switch Profile or create new Profile files on the server + * Switch Profile or create new Profile files on the server */ - + // switch/load new profile storage - + Profile newProfile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.NEW_PROFILE); String contestPassword = (String) PacketFactory.getObjectValue(packet, PacketFactory.CONTEST_PASSWORD); - + newProfile.setSiteNumber(contest.getSiteNumber()); if (contestPassword == null) { @@ -937,7 +939,7 @@ private void handleUpdateClientProfile(Packet packet, ConnectionHandlerID connec } ProfileManager manager = new ProfileManager(); - + if (manager.createProfilesPathandFiles(newProfile, contest.getSiteNumber(), contestPassword)) { /** @@ -955,52 +957,52 @@ private void handleUpdateClientProfile(Packet packet, ConnectionHandlerID connec * Then save the new contest information. */ contest2.storeConfiguration(controller.getLog()); - } - + } + if (manager.isProfileAvailable(newProfile, contest.getSiteNumber(), contestPassword.toCharArray())) { PacketFactory.dumpPacket(System.out, packet, "handleUpdateClientProfile switchProfile"); IInternalContest newContest = switchProfile(contest, newProfile, contestPassword.toCharArray(), false, packet, false); contest = newContest; - + sendStatusToServers (packet, newProfile); } else { throw new ProfileException("Can not switch profiles, invalid contest password"); } - + } else { /** * Client new profile. */ - + // All client data is reset then re-added from this packet contest.resetSubmissionData(); contest.resetConfigurationData(); - + unRegisterPlugins(); - + ContestLoader loader = new ContestLoader(); loader.loadDataIntoModel(contest, controller, packet, connectionHandlerID); loader = null; - + reRegisterPlugins(); - + info("handleUpdateClientProfile fireAllRefreshEvents start"); long start = new Date().getTime(); - + System.err.println("debug22 handleUpdateClientProfile fireAllRefreshEvents start "+new Date()); - + contest.fireAllRefreshEvents(); - + long elapsed = (new Date().getTime() - start) / 1000; - + System.err.println("debug22 handleUpdateClientProfile fireAllRefreshEvents done "+new Date()); System.err.println("debug22 handleUpdateClientProfile fireAllRefreshEvents elapsed "+elapsed); - + } } @@ -1008,7 +1010,7 @@ private void handleUpdateClientProfile(Packet packet, ConnectionHandlerID connec * Remove all listeners from model. */ private void unRegisterPlugins() { - contest.removeAllListeners(); + contest.removeAllListeners(); } @@ -1034,14 +1036,14 @@ private void reRegisterPlugins() { private IInternalContest initializeContest(IStorage storage, int siteNumber) { - + IInternalContest newContest = new InternalContest(); - + newContest.setSiteNumber(siteNumber); newContest.setStorage(storage); - + newContest.initializeSubmissions(siteNumber); - + return newContest; } @@ -1058,7 +1060,7 @@ private IStorage createStorage(Profile newProfile, String contestPassword) throw if (!new File(profilePath).isDirectory()) { throw new ProfileCloneException("Unable to use profile dir " + profilePath); } - + String databaseDirectoryName = profilePath + File.separator + "db." + contest.getSiteNumber(); try { @@ -1066,7 +1068,7 @@ private IStorage createStorage(Profile newProfile, String contestPassword) throw } catch (Exception e) { throw new ProfileCloneException("Unable to create DB dir " + profilePath, e); } - + FileSecurity fileSecurity = new FileSecurity(databaseDirectoryName); try { @@ -1074,49 +1076,49 @@ private IStorage createStorage(Profile newProfile, String contestPassword) throw } catch (Exception e) { throw new ProfileCloneException(e); } - + return fileSecurity; } /** * Create profile from the input packet. - * - * - * + * + * + * * @param newProfile the profile to store/load/create. * @param contest2 the contest where settings will be loaded. * @param packet a {@link edu.csus.ecs.pc2.core.packet.PacketType.Type#UPDATE_CLIENT_PROFILE}. - * @param connectionHandlerID - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @param connectionHandlerID + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void createProfileFromPacket(Profile newProfile, IInternalContest contest2, Packet packet, ConnectionHandlerID connectionHandlerID) throws IOException, ClassNotFoundException, FileSecurityException { - + if (packet.getType().equals(Type.UPDATE_CLIENT_PROFILE)){ ContestLoader loader = new ContestLoader(); loader.loadDataIntoModel(contest2, controller, packet, connectionHandlerID); loader = null; - + } else { new ProfileException("Can not create profile from packet type "+packet.getType().toString()); } - + } /** * RunFiles from a remote site, add these to this site. - * + * * @param packet * @param connectionHandlerID - * @throws Exception + * @throws Exception */ private void handleRunFilesList(Packet packet, ConnectionHandlerID connectionHandlerID) throws Exception { RunFiles[] files = (RunFiles[]) PacketFactory.getObjectValue(packet, PacketFactory.RUN_FILES_LIST); - + if (files != null) { for (RunFiles runFiles : files) { @@ -1134,11 +1136,11 @@ private void handleRunFilesList(Packet packet, ConnectionHandlerID connectionHan } else { throw new Exception("RUN_FILES are null from packet "+packet); } - + } - + private void handleFetchRunFiles(Packet packet, ConnectionHandlerID connectionHandlerID) { - + int siteNumber = packet.getSourceId().getSiteNumber(); int lastRunId = (Integer) PacketFactory.getObjectValue(packet, PacketFactory.RUN_ID); sendRunFilesToServer(siteNumber, lastRunId); @@ -1146,80 +1148,80 @@ private void handleFetchRunFiles(Packet packet, ConnectionHandlerID connectionHa } private void handleCloneProfile(Packet packet, ConnectionHandlerID connectionHandlerID) { - + // FIXME code security check only Admins can change profiles - + // prop.put(CLIENT_ID, source); // ClientId adminClientId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); - + // prop.put(PROFILE, profile2); // Profile currentProfile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE); - + try { ProfileCloneSettings settings = (ProfileCloneSettings) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE_CLONE_SETTINGS); boolean switchProfileNow = ((Boolean) PacketFactory.getObjectValue(packet, PacketFactory.SWITCH_PROFILE)).booleanValue(); - + Profile newProfile = cloneContest (packet, settings, switchProfileNow); settings.setProfilePath(newProfile.getProfilePath()); - + notifyAllOfClonedContest(packet, newProfile, settings); } catch (Exception e) { sendMessage(Area.PROFILES, "Unable to clone profile",e); info(e); } } - + private Profile cloneContest (Packet packet, ProfileCloneSettings settings, boolean switchProfileNow) { - + Profile newProfile = new Profile(settings.getName()); newProfile.setDescription(settings.getDescription()); newProfile.setSiteNumber(contest.getSiteNumber()); - + if (settings.getProfilePath() != null){ newProfile.setProfilePath(settings.getProfilePath()); } info("Start clone to profile "+newProfile.getName()); - + Profile addedProfile = contest.addProfile(newProfile); - + InternalContest newContest = new InternalContest(); newContest.setSiteNumber(contest.getSiteNumber()); - + try { /** * This clones the existing contest based on the settings, * including copying and saving all settings on disk. */ contest.clone(newContest, addedProfile, settings); - + contest.storeConfiguration(controller.getLog()); storeProfiles(); - + if (switchProfileNow ){ switchProfile(contest, newProfile, contest.getContestPassword().toCharArray(), true); } - + info("Done clone to profile "+newProfile.getName()); - + } catch (Exception e) { sendMessage(Area.PROFILES, "Unable to clone using packet "+packet,e); info("Failed to clone to profile"); info(e); } - + return newProfile; - + } private void handleServerSettings(Packet packet, ConnectionHandlerID connectionHandlerID) { - + ContestLoader loader = new ContestLoader(); loadSettingsFromRemoteServer(loader, packet, connectionHandlerID); loader = null; - + info(" handlePacket SERVER_SETTINGS - from another site -- all settings loaded " + packet); if (isServer()) { @@ -1228,7 +1230,7 @@ private void handleServerSettings(Packet packet, ConnectionHandlerID connectionH } /** - * + * * @param packet */ private void handleRunNotAvailable(Packet packet) { @@ -1243,7 +1245,7 @@ private void handleRunNotAvailable(Packet packet) { } } } - + private void handleRunUnCheckout(Packet packet, ConnectionHandlerID connectionHandlerID) throws IOException, ClassNotFoundException, FileSecurityException { Run run = (Run) PacketFactory.getObjectValue(packet, PacketFactory.RUN); ClientId whoCanceledId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); @@ -1252,7 +1254,7 @@ private void handleRunUnCheckout(Packet packet, ConnectionHandlerID connectionHa private void handleRunSubmissionConfirmation(Packet packet) throws IOException, ClassNotFoundException, FileSecurityException { Run run = (Run) PacketFactory.getObjectValue(packet, PacketFactory.RUN); - + contest.addRun(run); if (isServer()) { controller.sendToJudgesAndOthers(packet, isThisSite(run)); @@ -1261,9 +1263,9 @@ private void handleRunSubmissionConfirmation(Packet packet) throws IOException, /** * Handles a reset all contest from admin. - * + * * Checks security that allows this client (Admin hopefully) to reset this site and then send reset to all other sites. - * + * * @param packet * @param connectionHandlerID * @throws ContestSecurityException @@ -1300,10 +1302,10 @@ private void resetAllSites(Packet packet, ConnectionHandlerID connectionHandlerI /** * Reset the contest. - * + * * Clone current profile, prepend with the name "Backup of " and mark inactive, * then switch to the newly cloned profile. - * + * * @param packet * @param profile * @throws ProfileCloneException @@ -1313,27 +1315,27 @@ private void resetAllSites(Packet packet, ConnectionHandlerID connectionHandlerI * @throws FileSecurityException */ private void resetContest(Packet packet, Profile profile) throws ProfileCloneException, ProfileException, IOException, ClassNotFoundException, FileSecurityException { - + Boolean eraseProblems = (Boolean) PacketFactory.getObjectValue(packet, PacketFactory.DELETE_PROBLEM_DEFINITIONS); Boolean eraseLanguages = (Boolean) PacketFactory.getObjectValue(packet, PacketFactory.DELETE_LANGUAGE_DEFINITIONS); if (isServer()) { ClientId adminClientId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); - + if (! contest.isAllowed(adminClientId, Permission.Type.SWITCH_PROFILE)){ info("permission is not granted to "+adminClientId+" to reset"); } else { info("permission is granted to "+adminClientId+" to reset"); } - + /** * Hide the current copy of this profile, give it a new name. */ Profile updatedProfile = contest.getProfile(); updatedProfile.setActive (false); updatedProfile.setName("Backup of "+updatedProfile.getName()); - + /** * This clears all submission data and counters. */ @@ -1343,57 +1345,57 @@ private void resetContest(Packet packet, Profile profile) throws ProfileCloneExc if (newContest.getSiteNumber() == 0){ newContest.setSiteNumber(contest.getSiteNumber()); } - + newContest.addProfile(updatedProfile); // Add old hiddent profile into new profile's list - + String title = contest.getContestInformation().getContestTitle(); String description = profile.getDescription(); String password = contest.getContestPassword(); ProfileCloneSettings settings = new ProfileCloneSettings(profile.getName(), description, password.toCharArray(), contest.getProfile()); - + settings.setContestTitle(title); - + settings.setResetContestTimes( true ); settings.setCopyAccounts( true ); settings.setCopyContestSettings( true ); settings.setCopyGroups( true ); settings.setCopyJudgements( true ); settings.setCopyNotifications( true ); - + settings.setCopyLanguages( !eraseLanguages ); settings.setCopyProblems( !eraseProblems ); settings.setCopyRuns( false ); settings.setCopyClarifications( false ); - + settings.setCopyCategories( false ); settings.setContestPassword(contest.getContestPassword().toCharArray()); - + Profile newProfile = cloneContest (packet, settings, true); settings.setProfilePath(newProfile.getProfilePath()); - + notifyAllOfClonedContest(packet, newProfile, settings); - - + + } else { - + // SOMEDAY remove this unused code (with profiles this code is no longer used) - + // Set Contest Profile contest.setProfile(profile); - + resetContestData(eraseProblems, eraseLanguages); - + contest.fireAllRefreshEvents(); } } - + private void notifyAllOfClonedContest(Packet packet, Profile newProfile, ProfileCloneSettings settings) { contest.addProfile(newProfile); Packet addPacket = PacketFactory.createAddSetting(contest.getClientId(), PacketFactory.ALL_SERVERS, newProfile); controller.sendToJudgesAndOthers(addPacket, false); - + if (isThisSite(packet.getSourceId())) { // Only send to other servers if this server originated the clone. controller.sendToServers(packet); @@ -1403,12 +1405,12 @@ private void notifyAllOfClonedContest(Packet packet, Profile newProfile, Profile /** * Clear all auto judge problems for this contest site/client. - * + * */ private void removeAllProblemsFromAutoJudging(){ Vector vectorAccounts = contest.getAccounts(ClientType.Type.JUDGE, contest.getSiteNumber()); - Account [] accounts = (Account[]) vectorAccounts.toArray(new Account[vectorAccounts.size()]); - + Account [] accounts = vectorAccounts.toArray(new Account[vectorAccounts.size()]); + for (Account account : accounts){ ClientSettings clientSettings = new ClientSettings(account.getClientId()); clientSettings.setAutoJudging(false); @@ -1416,7 +1418,7 @@ private void removeAllProblemsFromAutoJudging(){ } } - private void resetContestData(Boolean eraseProblems, Boolean eraseLanguages) { + private void resetContestData(Boolean eraseProblems, Boolean eraseLanguages) { contest.resetSubmissionData(); @@ -1424,7 +1426,7 @@ private void resetContestData(Boolean eraseProblems, Boolean eraseLanguages) { for (Problem problem : contest.getProblems()) { contest.deleteProblem(problem); } - + removeAllProblemsFromAutoJudging(); } @@ -1434,12 +1436,12 @@ private void resetContestData(Boolean eraseProblems, Boolean eraseLanguages) { } } } - + /** * Get a SERVER client id. - * + * * This is a generic send to all server and clients ClientId. - * + * * @return a generic all sites server client id */ private ClientId getServerClientId() { @@ -1451,15 +1453,15 @@ private void handleRunExecutionStatus(Packet packet, ConnectionHandlerID connect Run run = (Run) PacketFactory.getObjectValue(packet, PacketFactory.RUN); ClientId judgeClientId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); RunExecutionStatus status = (RunExecutionStatus) PacketFactory.getObjectValue(packet, PacketFactory.RUN_STATUS); - + if (isServer()) { if (!isThisSite(judgeClientId)) { - + // If this is not a run status from this site, then send to spectators/API only - + sendToSpectatorsAndSites(packet, false); - + } else { // packet from this site, send to all spectators/API and to other servers. @@ -1476,13 +1478,13 @@ private void requestFetchedRun(Packet packet, ConnectionHandlerID connectionHand Run run = (Run) PacketFactory.getObjectValue(packet, PacketFactory.RUN); ClientId whoRequestsRunId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); - + securityCheck(Permission.Type.ALLOWED_TO_FETCH_RUN, whoRequestsRunId, connectionHandlerID); - + if (isServer()) { if (!isThisSite(run)) { - + ClientId serverClientId = new ClientId(run.getSiteNumber(), ClientType.Type.SERVER, 0); if (contest.isLocalLoggedIn(serverClientId) || contest.isRemoteLoggedIn(serverClientId)) { @@ -1531,8 +1533,8 @@ private void runAvailable(Packet packet) throws IOException, ClassNotFoundExcept private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID connectionHandlerID) throws IOException, ClassNotFoundException, FileSecurityException, ContestSecurityException { Run submittedRun = (Run) PacketFactory.getObjectValue(packet, PacketFactory.RUN); RunFiles runFiles = (RunFiles) PacketFactory.getObjectValue(packet, PacketFactory.RUN_FILES); - - + + Long overrideElapsedTime = (Long) PacketFactory.getObjectValue(packet, PacketFactory.ELAPSED_TIME); if (overrideElapsedTime != null) { if (contest.getContestInformation().isCcsTestMode()) { @@ -1546,7 +1548,7 @@ private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID c throw new SecurityException("Attempted to use time override in submit run when not in CCS Test Mode"); } } - + Long overrideRunId = (Long) PacketFactory.getObjectValue(packet, PacketFactory.OVERRIDE_RUN_ID); if (overrideRunId != null) { if (contest.getContestInformation().isCcsTestMode()) { @@ -1558,10 +1560,10 @@ private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID c throw new SecurityException("Attempted to use run id override in submit run when not in CCS Test Mode"); } } - + ClientId submitter = submittedRun.getSubmitter(); boolean proxySubmission = ! submitter.equals(fromId); - + if (!isServer(fromId)){ if (proxySubmission) { @@ -1585,9 +1587,9 @@ private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID c } } - + Run run = contest.acceptRun(submittedRun, runFiles); - + /** * There are three conditions where a run would be added to the system but not appear on the scoreboard (or team's display): * 1 - past end of contest (remaining time is zero or less) @@ -1611,7 +1613,7 @@ private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID c Packet dupSubmissionPacket = PacketFactory.createRunSubmissionConfirmation(contest.getClientId(), fromId, run, runFiles); controller.sendToServers(dupSubmissionPacket); } - + controller.sendRunToSubmissionInterface(run, runFiles); } @@ -1642,7 +1644,7 @@ private void runRequest(Packet packet, ConnectionHandlerID connectionHandlerID) } else { requestRun(packet, run, requestFromId, connectionHandlerID, computerJudge); } - + } private void clockStopped(Packet packet) { @@ -1672,31 +1674,31 @@ private void startClock(Packet packet) { } private void handlePasswordChangeResults(Packet packet) { - + ClientId clientId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); Boolean passwordChanged = (Boolean) PacketFactory.getObjectValue(packet, PacketFactory.PASSWORD_CHANGED); String message = (String) PacketFactory.getObjectValue(packet, PacketFactory.MESSAGE_STRING); - + String mess; if (passwordChanged.booleanValue()){ mess = "Password changed "+ message; } else { mess = "Password NOT changed "+ message; } - + controller.getLog().log(Log.INFO, mess); - + contest.passwordChanged (passwordChanged.booleanValue(), clientId, message); - + } /** * Change password request from client. - * + * * This change assumes only that a client is changing their own password. - * If the client requesting is not from this site, their password will NOT + * If the client requesting is not from this site, their password will NOT * be changed. - * + * * @param packet input change password packet */ private void attemptChangePassword(Packet packet) { @@ -1735,7 +1737,7 @@ private void attemptChangePassword(Packet packet) { sendPasswordResultsBackToClient(clientId, false, "Can not change password from site " + clientId); } else { - + try { if (contest.isValidLoginAndPassword(clientId, password)) { @@ -1744,7 +1746,7 @@ private void attemptChangePassword(Packet packet) { Account account = contest.getAccount(clientId); account.setPassword(newPassword); contest.updateAccount(account); - + account = contest.getAccount(clientId); sendPasswordResultsBackToClient(clientId, true, "Password changed"); @@ -1763,13 +1765,13 @@ private void attemptChangePassword(Packet packet) { /** * Send password results back to client. - * + * * @param clientId client who requested password change. * @param changed was password changed? * @param message error or confirm message. */ private void sendPasswordResultsBackToClient(ClientId clientId, boolean changed, String message) { - + Packet passwordChangeResult = PacketFactory.createPasswordChangeResult(clientId, clientId, changed, message); controller.sendToClient(passwordChangeResult); } @@ -1834,9 +1836,9 @@ protected void checkoutClarification(Packet packet, ConnectionHandlerID connecti /** * Checks whether client is allowed to do particular activity +(Permission). - * + * * This checks the client permissions settings and if the client does not have permission to do the permission (type) throws a security exception. - * + * * @param type * @param clientId * @param connectionHandlerID @@ -1864,12 +1866,12 @@ private void acceptRunJudgement(Packet packet, ConnectionHandlerID connectionHan /** * Process checkout run packets. - * + * * @param packet * @param packetType either {@link Type.RUN_CHECKOUT} or {@link Type.RUN_CHECKOUT_NOTIFICATION} - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void runCheckout(Packet packet, Type packetType) throws IOException, ClassNotFoundException, FileSecurityException { @@ -1887,12 +1889,12 @@ private void runCheckout(Packet packet, Type packetType) throws IOException, Cla break; case RUN_CHECKOUT_NOTIFICATION: - + // only process this notification if the run was checked out by someone else if (!contest.getClientId().equals(whoCheckedOut)) { contest.updateRun(run, whoCheckedOut); } - + break; default: controller.getLog().log(Log.WARNING, "Attempted to runCheckout with packet: " + packet); @@ -1912,22 +1914,22 @@ private void handleFetchedRun (Packet packet, ConnectionHandlerID connectionHand ClientId whoCheckedOut = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); RunResultFiles[] runResultFiles = (RunResultFiles[]) PacketFactory.getObjectValue(packet, PacketFactory.RUN_RESULTS_FILE); contest.updateRun(run, runFiles, whoCheckedOut, runResultFiles); - + // If this is the server, make sure the reply gets to the correct local client. if(isServer()) { controller.sendToClient(packet); } } - + /** * Re-judge run request, parse packet, attempt to checkout run. - * + * * @param packet * @param connectionHandlerID * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void requestRejudgeRun(Packet packet, ConnectionHandlerID connectionHandlerID) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { @@ -1999,7 +2001,7 @@ private void reconnectSite(Packet packet) { } private void handleMessagePacket(Packet packet) throws Exception { - + if (isServer()){ String message = (String) PacketFactory.getObjectValue(packet, PacketFactory.MESSAGE_STRING); @@ -2014,9 +2016,9 @@ private void handleMessagePacket(Packet packet) throws Exception { int siteNumber = packet.getDestinationId().getSiteNumber(); controller.sendToRemoteServer(siteNumber, messagePacket); } - + contest.addMessage(area, packet.getSourceId(), packet.getDestinationId(), message); - + } else { String message = (String) PacketFactory.getObjectValue(packet, PacketFactory.MESSAGE_STRING); Area area = (Area) PacketFactory.getObjectValue(packet, PacketFactory.MESSAGE_AREA); @@ -2027,12 +2029,12 @@ private void handleMessagePacket(Packet packet) throws Exception { } } } - + /** * Insure that directory exists. - * + * * Will use File.mkdirs() if needed to create directory. - * + * * @param directoryName */ private void insureDirectory(String directoryName) { @@ -2043,7 +2045,7 @@ private void insureDirectory(String directoryName) { /** * Login to server success. - * + * * @param packet * @param connectionHandlerID * @param fromId @@ -2052,7 +2054,7 @@ private void insureDirectory(String directoryName) { * @throws FileSecurityException */ private synchronized void loginSuccess(Packet packet, ConnectionHandlerID connectionHandlerID, ClientId fromId) throws IOException, ClassNotFoundException, FileSecurityException { - + ClientId clientId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); if (!contest.isLoggedIn()) { @@ -2065,17 +2067,17 @@ private synchronized void loginSuccess(Packet packet, ConnectionHandlerID connec System.err.println("FATAL ERROR - Contest Security Password is null "); System.exit(44); } - + Profile theProfile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE); if (theProfile == null) { StaticLog.getLog().log(Log.SEVERE, "FATAL ERROR - Profile is null"); System.err.println("FATAL ERROR - Profile is null "); System.exit(44); } - + insureDirectory(theProfile.getProfilePath()); theProfile.setSiteNumber(clientId.getSiteNumber()); - + String baseDirectoryName = theProfile.getProfilePath() + File.separator + "db." + clientId.getSiteNumber(); insureDirectory(baseDirectoryName); @@ -2113,11 +2115,11 @@ private synchronized void loginSuccess(Packet packet, ConnectionHandlerID connec contest.setSiteNumber(clientId.getSiteNumber()); ContestLoader loader = new ContestLoader(); loader.loadDataIntoModel(contest, controller, packet, connectionHandlerID); - + if (isServer()) { - + storeProfiles(); - + loader.loadIfMissingAccountToModel(contest,controller, packet, edu.csus.ecs.pc2.core.model.ClientType.Type.TEAM); loader.loadIfMissingAccountToModel(contest,controller, packet, edu.csus.ecs.pc2.core.model.ClientType.Type.JUDGE); loader.loadIfMissingAccountToModel(contest,controller, packet, edu.csus.ecs.pc2.core.model.ClientType.Type.SCOREBOARD); @@ -2144,7 +2146,7 @@ private synchronized void loginSuccess(Packet packet, ConnectionHandlerID connec // Send settings packet to the server we logged into controller.sendToClient(createContestSettingsPacket(packet.getSourceId())); - + sendRequestForRunfFiles (packet, packet.getSourceId().getSiteNumber()); } @@ -2160,7 +2162,7 @@ private synchronized void loginSuccess(Packet packet, ConnectionHandlerID connec ContestLoader loader = new ContestLoader(); loadSettingsFromRemoteServer(loader, packet, connectionHandlerID); loader = null; - + contest.storeConfiguration(controller.getLog()); controller.sendToClient(createContestSettingsPacket(packet.getSourceId())); @@ -2186,7 +2188,7 @@ private void startEvalLog() { /** * Dump both local and remote server logins. - * + * * @param comment */ private void dumpServerLoginLists(String comment) { @@ -2285,12 +2287,12 @@ private void sendForceDisconnection(Packet packet) { /** * Update from admin to server. - * + * * @param packet * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void updateRun(Packet packet, ConnectionHandlerID connectionHandlerID) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { @@ -2337,13 +2339,13 @@ private void updateRun(Packet packet, ConnectionHandlerID connectionHandlerID) t Run theRun = contest.getRun(run.getElementId()); Packet runUpdatedPacket = PacketFactory.createRunUpdateNotification(contest.getClientId(), PacketFactory.ALL_SERVERS, theRun, whoChangedRun); controller.sendToJudgesAndOthers(runUpdatedPacket, true); - + /** * Send Judgement Notification to Team or not. */ if (theRun.isJudged() && theRun.getJudgementRecord().isSendToTeam()) { - + Packet notifyPacket = PacketFactory.clonePacket(contest.getClientId(), run.getSubmitter(), runUpdatedPacket); sendJudgementToTeam (notifyPacket, theRun); } @@ -2362,7 +2364,7 @@ private void updateRun(Packet packet, ConnectionHandlerID connectionHandlerID) t /** * Login from a server, - * + * * @param packet */ private void loginClient(Packet packet) { @@ -2391,7 +2393,7 @@ private void loginClient(Packet packet) { } contest.addClientSettings(clientSettings); } - + } else { controller.getLog().log(Log.DEBUG, "LOGIN packet, server site " + whoLoggedIn + " logged onto " + packet.getSourceId() + ", already logged in on this site"); } @@ -2416,20 +2418,20 @@ private void logoutClient(Packet packet) { if (isServer()) { // TODO SECURITY code - only allow certain users to logoff other users - // TEST CASE - attempt to logoff client as say a team + // TEST CASE - attempt to logoff client as say a team // throw new SecurityException("Client " + contest.getClientId() + " attempted to logoff another client "+whoLoggedOff); - + if (isServer(whoLoggedOff)){ // Special logic, ignore any logoff from any client about any server. // Only disconnect logic will work on a server. - + controller.getLog().info("No logoff server allowed, logoff packet "+packet+" ignored"); - + } else if (contest.isLocalLoggedIn(whoLoggedOff)) { // Logged into this server, so we log them off and send out packet. controller.logoffUser(whoLoggedOff); - + } else { // Log them off, only notify local clients. if (isServer(packet.getSourceId()) && whoLoggedOff.getSiteNumber() == packet.getSourceId().getSiteNumber()){ @@ -2455,11 +2457,11 @@ private void logoutClient(Packet packet) { /** * Send judgement to judges, servers, admins and boards. - * + * * @param packet - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void sendJudgementUpdate(Packet packet) throws IOException, ClassNotFoundException, FileSecurityException { @@ -2498,11 +2500,11 @@ private void sendAnswerClarification(Packet packet) { /** * Update from server to everyone else. - * + * * @param packet - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void sendRunUpdateNotification(Packet packet) throws IOException, ClassNotFoundException, FileSecurityException { @@ -2519,11 +2521,11 @@ private void sendRunUpdateNotification(Packet packet) throws IOException, ClassN /** * Generate local accounts for forward this request to another server. - * + * * @param packet - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void generateAccounts(Packet packet) throws IOException, ClassNotFoundException, FileSecurityException { @@ -2539,7 +2541,7 @@ private void generateAccounts(Packet packet) throws IOException, ClassNotFoundEx // get vector of new accounts. Vector accountVector = contest.generateNewAccounts(type.toString(), count.intValue(), startCount.intValue(), active); - Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + Account[] accounts = accountVector.toArray(new Account[accountVector.size()]); contest.storeConfiguration(controller.getLog()); @@ -2558,13 +2560,13 @@ private void generateAccounts(Packet packet) throws IOException, ClassNotFoundEx /** * Starts the contest clock and sends notification to other servers/clients. - * + * * @param packet * @param connectionHandlerID * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void startContest(Packet packet, ConnectionHandlerID connectionHandlerID) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { @@ -2604,12 +2606,12 @@ private void startContest(Packet packet, ConnectionHandlerID connectionHandlerID /** * This stops the contest and sends notification to other servers/clients. - * + * * @param connectionHandlerID * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void stopContest(Packet packet, ConnectionHandlerID connectionHandlerID) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { ClientId who = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); @@ -2655,11 +2657,11 @@ private void deleteSetting(Packet packet) { /** * Add a new setting from another server. - * + * * @param packet - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void addNewSetting(Packet packet) throws IOException, ClassNotFoundException, FileSecurityException { @@ -2711,7 +2713,7 @@ private void addNewSetting(Packet packet) throws IOException, ClassNotFoundExcep } sendToTeams = true; } - + Problem [] problems = (Problem []) PacketFactory.getObjectValue(packet, PacketFactory.PROBLEM_LIST); if (problems != null){ addNewProblems (contest, packet, problems); @@ -2729,12 +2731,12 @@ private void addNewSetting(Packet packet) throws IOException, ClassNotFoundExcep contest.addBalloonSettings(balloonSettings); sendToTeams = true; } - + Profile profile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE); if (profile != null) { contest.addProfile(profile); } - + Packet updatePacket = null; Account oneAccount = (Account) PacketFactory.getObjectValue(packet, PacketFactory.ACCOUNT); @@ -2742,7 +2744,7 @@ private void addNewSetting(Packet packet) throws IOException, ClassNotFoundExcep if (isServer()) { if (isThisSite(oneAccount)) { ClientId clientId = oneAccount.getClientId(); - + // Add account, this assigns the new account a client number. Vector accountVector = contest.generateNewAccounts(clientId.getClientType().toString(), 1, true); Account addedAccount = accountVector.firstElement(); @@ -2816,11 +2818,11 @@ private void addNewSetting(Packet packet) throws IOException, ClassNotFoundExcep } if (isServer()) { - + contest.storeConfiguration(controller.getLog()); - + storeProfiles(); - + boolean sendToOtherServers = isThisSite(packet.getSourceId().getSiteNumber()); if (updatePacket != null) { @@ -2837,9 +2839,9 @@ private void addNewSetting(Packet packet) throws IOException, ClassNotFoundExcep /** * Add problems and problem data file list to contest. - * + * * @param contest2 - * @param packet contains ProblemDataFiles + * @param packet contains ProblemDataFiles * @param problems problems to add */ protected void addNewProblems(IInternalContest iContest, Packet packet, Problem[] problems) { @@ -2860,16 +2862,16 @@ protected void addNewProblems(IInternalContest iContest, Packet packet, Problem[ /** * Handle a UPDATE_SETTING packet. - * + * * @param packet - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void updateSetting(Packet packet) throws IOException, ClassNotFoundException, FileSecurityException { boolean sendToTeams = false; - + Packet oneUpdatePacket = null; Site site = (Site) PacketFactory.getObjectValue(packet, PacketFactory.SITE); @@ -2930,7 +2932,7 @@ private void updateSetting(Packet packet) throws IOException, ClassNotFoundExcep contest.updateBalloonSettings(balloonSettings); sendToTeams = true; } - + Profile profile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE); if (profile != null) { contest.updateProfile(profile); @@ -2986,13 +2988,13 @@ private void updateSetting(Packet packet) throws IOException, ClassNotFoundExcep } } } - + PlaybackInfo playbackInfo = (PlaybackInfo) PacketFactory.getObjectValue(packet, PacketFactory.PLAYBACK_INFO); if (playbackInfo != null) { if (isServer()) { boolean isStarted = contest.getPlaybackManager().getPlaybackInfo().isStarted(); PlaybackInfo currentPlaybackInfo = updatePlaybackInfo (playbackInfo); - + if (isStarted != currentPlaybackInfo.isStarted()) { if (currentPlaybackInfo.isStarted()) { contest.startReplayPlaybackInfo(currentPlaybackInfo); @@ -3002,7 +3004,7 @@ private void updateSetting(Packet packet) throws IOException, ClassNotFoundExcep } else { contest.updatePlaybackInfo(currentPlaybackInfo); } - + } else { contest.updatePlaybackInfo(playbackInfo); } @@ -3030,7 +3032,7 @@ private void updateSetting(Packet packet) throws IOException, ClassNotFoundExcep } } } - + FinalizeData finalizeData = (FinalizeData) PacketFactory.getObjectValue(packet, PacketFactory.FINALIZE_DATA); if (finalizeData != null) { contest.setFinalizeData(finalizeData); @@ -3055,9 +3057,9 @@ private void updateSetting(Packet packet) throws IOException, ClassNotFoundExcep if (isServer()) { contest.storeConfiguration(controller.getLog()); - + boolean sendToOtherServers = isThisSite(packet.getSourceId().getSiteNumber()); - + if (oneUpdatePacket != null) { controller.sendToJudgesAndOthers(oneUpdatePacket, sendToOtherServers); } else { @@ -3070,7 +3072,7 @@ private void updateSetting(Packet packet) throws IOException, ClassNotFoundExcep } } } - + private boolean handleLanguageList(Language[] languages) { boolean sendToTeams = false; if (languages != null) { @@ -3216,13 +3218,13 @@ public void cancelRun(Packet packet, Run run, ClientId whoCanceledRun, Connectio /** * UN checkout or cancel clarification checkout. - * + * * @param packet * @param connectionHandlerID * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ public void cancelClarificationCheckOut(Packet packet, ConnectionHandlerID connectionHandlerID) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { Clarification clarification = (Clarification) PacketFactory.getObjectValue(packet, PacketFactory.CLARIFICATION); @@ -3308,18 +3310,18 @@ private void answerClarification(Packet packet, ConnectionHandlerID connectionHa /** * This method sends the specified judgement to all clients that need to know about it. - * + * * @param run * @param judgementRecord * @param runResultFiles * @param whoJudgedId * @param connectionHandlerID * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ - protected void sendRunJudgementToClients(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles, + protected void sendRunJudgementToClients(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles, ClientId whoJudgedId, ConnectionHandlerID connectionHandlerID, Packet packet) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { if (isServer()) { @@ -3342,10 +3344,10 @@ protected void sendRunJudgementToClients(Run run, JudgementRecord judgementRecor Run theRun = contest.getRun(run.getElementId()); try { - + //try to send the judgement to the team if (judgementRecord.isComputerJudgement()) { - + if (contest.getProblem(theRun.getProblemId()).isManualReview()) { if (contest.getProblem(theRun.getProblemId()).isPrelimaryNotification()) { @@ -3355,7 +3357,7 @@ protected void sendRunJudgementToClients(Run run, JudgementRecord judgementRecor rrf = null; } Packet judgementPacket = PacketFactory.createRunJudgement(contest.getClientId(), run.getSubmitter(), theRun, judgementRecord, rrf); - + sendJudgementToTeam (judgementPacket, theRun); } } else { @@ -3379,11 +3381,11 @@ protected void sendRunJudgementToClients(Run run, JudgementRecord judgementRecor sendJudgementToTeam (judgementPacket, theRun); } } catch (Exception e) { - + controller.getLog().log(Level.WARNING, "Exception attempting to send judgement for run " + run + " in packet " + packet + ": "+ e.getMessage(), e); } - //try to send the judgement to other clients + //try to send the judgement to other clients Packet judgementUpdatePacket = PacketFactory.createRunJudgmentUpdate(contest.getClientId(), PacketFactory.ALL_SERVERS, theRun, whoJudgedId); controller.sendToJudgesAndOthers(judgementUpdatePacket, true); } @@ -3399,10 +3401,10 @@ protected void sendRunJudgementToClients(Run run, JudgementRecord judgementRecor * @param run */ private void sendJudgementToTeam(Packet judgementPacket, Run run) { - + if (run.isJudged() && run.getJudgementRecord().isSendToTeam()) { JudgementNotificationsList judgementNotificationsList = contest.getContestInformation().getJudgementNotificationsList(); - + if (! RunUtilities.supppressJudgement(judgementNotificationsList, run, contest.getContestTime())){ // Send to team who sent it, send to other server if needed. controller.sendToClient(judgementPacket); @@ -3416,21 +3418,21 @@ private void sendJudgementToTeam(Packet judgementPacket, Run run) { /** * Checkout a run. - * + * * @see #checkoutRun(Packet, Run, ClientId, boolean) * @param packet * @param run * @param whoRequestsRunId * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void requestRun(Packet packet, Run run, ClientId whoRequestsRunId, ConnectionHandlerID connectionHandlerID, boolean computerJudge) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { checkoutRun(packet, run, whoRequestsRunId, false, computerJudge, connectionHandlerID); } - + private void requestClarification(Packet packet, ConnectionHandlerID connectionHandlerID) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { ElementId clarificationId = (ElementId) PacketFactory.getObjectValue(packet, PacketFactory.REQUESTED_CLARIFICATION_ELEMENT_ID); ClientId requestFromId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID); @@ -3493,13 +3495,13 @@ private void requestClarification(Packet packet, ConnectionHandlerID connectionH /** * Fetch a run. - * + * * Either checks out run (marks as {@link edu.csus.ecs.pc2.core.contest.Run.RunStates#BEING_JUDGED BEING_JUDGED}) and send to everyone, or send a * {@link edu.csus.ecs.pc2.core.packet.PacketType.Type#RUN_NOTAVAILABLE RUN_NOTAVAILABLE}. *

* If readOnly is false, will checkout run.
* if readOnly is true will fetch run without setting the run as "being judged". - * + * * @param packet * @param run * @param whoRequestsRunId @@ -3507,9 +3509,9 @@ private void requestClarification(Packet packet, ConnectionHandlerID connectionH * get a read only copy (aka do not checkout/select run). * @param connectionHandlerID * @throws ContestSecurityException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ private void checkoutRun(Packet packet, Run run, ClientId whoRequestsRunId, boolean readOnly, boolean computerJudge, ConnectionHandlerID connectionHandlerID) throws ContestSecurityException, IOException, ClassNotFoundException, FileSecurityException { @@ -3541,21 +3543,21 @@ private void checkoutRun(Packet packet, Run run, ClientId whoRequestsRunId, bool // just get run and sent it to them. theRun = contest.getRun(run.getElementId()); - + RunFiles runFiles = null; - + // in case GetRunFiles throws an exception we want to deal with it separately try { - runFiles = contest.getRunFiles(run); + runFiles = contest.getRunFiles(run); } catch (Exception e) { controller.getLog().warning("contest.getRunFiles (R/O) can not get files for run " + run.getNumber() + ": " + e.getMessage()); - + // set status to NEW indicating there was a failure and it has to be manually taken care of // the judges will be notifed of a new run. theRun.setStatus(Run.RunStates.NEW); Packet availableRunPacket = PacketFactory.createRunAvailable(contest.getClientId(), whoRequestsRunId, theRun); controller.sendToJudgesAndOthers(availableRunPacket, true); - + Packet notAvailableRunPacket = PacketFactory.createRunNotAvailable(contest.getClientId(), whoRequestsRunId, theRun); controller.sendToClient(notAvailableRunPacket); @@ -3576,13 +3578,13 @@ private void checkoutRun(Packet packet, Run run, ClientId whoRequestsRunId, bool theRun = contest.checkoutRun(run, whoRequestsRunId, false, computerJudge); RunFiles runFiles = null; - + // in case GetRunFiles throws an exception we want to deal with it separately try { - runFiles = contest.getRunFiles(run); + runFiles = contest.getRunFiles(run); } catch (Exception e) { controller.getLog().warning("contest.getRunFiles can not get files for run " + run.getNumber() + " (settng to status NEW): " + e.getMessage()); - + try { // cancel the checkout and set run state to NEW to notify judges contest.cancelRunCheckOut(run, whoRequestsRunId); @@ -3592,13 +3594,13 @@ private void checkoutRun(Packet packet, Run run, ClientId whoRequestsRunId, bool } catch (Exception e1) { controller.getLog().severe("Problem cancelling run checkout after error getting run " + run.getNumber() + " files." + e1); } - + Packet notAvailableRunPacket = PacketFactory.createRunNotAvailable(contest.getClientId(), whoRequestsRunId, theRun); controller.sendToClient(notAvailableRunPacket); return; } - + if (runFiles == null) { try { contest.cancelRunCheckOut(run, whoRequestsRunId); @@ -3615,7 +3617,7 @@ private void checkoutRun(Packet packet, Run run, ClientId whoRequestsRunId, bool Packet checkOutNotificationPacket = PacketFactory.createCheckedOutRunNotification(contest.getClientId(), whoRequestsRunId, theRun, whoRequestsRunId); controller.sendToJudgesAndOthers(checkOutNotificationPacket, true); - + } catch (RunUnavailableException runUnavailableException) { controller.getLog().info("runUnavailableException " + runUnavailableException.getMessage()); theRun = contest.getRun(run.getElementId()); @@ -3650,21 +3652,21 @@ private boolean isThisSite(Account account) { /** * Loads only the settings from the remote server into this server's model. - * + * * Loads settings from other server, submissions, etc. into model. - * + * * @param packet * @param connectionHandlerID */ public void loadSettingsFromRemoteServer(ContestLoader loader, Packet packet, ConnectionHandlerID connectionHandlerID) { - + int remoteSiteNumber = packet.getSourceId().getSiteNumber(); info("Start loading settings from remote site "+remoteSiteNumber); loader.addRemoteContestTimesToModel(contest, controller, packet, remoteSiteNumber); loader.addRemoteRunsToModel(contest, controller, packet); - + // bug 1295 only send this request if we are a server if (isServer()) { sendRequestForRunfFiles (packet, remoteSiteNumber); @@ -3673,7 +3675,7 @@ public void loadSettingsFromRemoteServer(ContestLoader loader, Packet packet, Co loader.addRemoteClarificationsToModel(contest, controller, packet, remoteSiteNumber); loader.addRemoteAccountsToModel(contest, controller, packet, remoteSiteNumber); - + loader.loadIfMissingAccountToModel(contest,controller, packet, edu.csus.ecs.pc2.core.model.ClientType.Type.TEAM); loader.loadIfMissingAccountToModel(contest,controller, packet, edu.csus.ecs.pc2.core.model.ClientType.Type.JUDGE); loader.loadIfMissingAccountToModel(contest,controller, packet, edu.csus.ecs.pc2.core.model.ClientType.Type.SCOREBOARD); @@ -3686,7 +3688,7 @@ public void loadSettingsFromRemoteServer(ContestLoader loader, Packet packet, Co loader.addAllConnectionIdsToModel(contest, controller, packet); loader.addRemoteLoginsToModel(contest, controller, packet, remoteSiteNumber); - + loader.addJudgementsToModel(contest, controller, packet); if (isServer()) { @@ -3698,26 +3700,26 @@ public void loadSettingsFromRemoteServer(ContestLoader loader, Packet packet, Co controller.getLog().log(Log.WARNING, ex.getMessage(), ex); } } - + controller.sendToJudgesAndOthers(packet, false); - + info("Done loading settings from remote site "+remoteSiteNumber); } /** * Send request for run files, if needed. - * + * * Determines whether this site has all run files for a remote site. * If there is a need to sync, will send a packet to sync with that * other site. - * + * * Send a packet to the remote server to retrieve all run submissions (files) from that remote server. - * + * * @param packet * @param remoteSiteNumber */ public void sendRequestForRunfFiles(Packet packet, int remoteSiteNumber) { - + info("sendRequestForRunfFiles for "+remoteSiteNumber); Run[] runs = (Run[]) PacketFactory.getObjectValue(packet, PacketFactory.RUN_LIST); @@ -3731,20 +3733,20 @@ public void sendRequestForRunfFiles(Packet packet, int remoteSiteNumber) { sendRunFilesRequestToServer(remoteSiteNumber, localLastRunId); info("sendRequestForRunfFiles to "+remoteSiteNumber+ " starting at run id # "+localLastRunId); } // else { no files to fetch - + } // else no runs from that site, ok. } - + /** * Get maximum run id for the site. - * + * * @param runs list of runs - * @param siteNumber + * @param siteNumber * @return 0 if no runs, else the last run id at the site. */ private int getLastRunId(Run[] runs, int siteNumber) { int maxRunId = 0; - + for (Run run : runs){ if (run.getSiteNumber() == siteNumber){ maxRunId = Math.max(run.getNumber(), maxRunId); @@ -3755,9 +3757,9 @@ private int getLastRunId(Run[] runs, int siteNumber) { /** * Send a request to remote server for RunFiles. - * + * * This sends a packet that request that any run id which is lastRunId or greater be sent to this server.
- * + * * @param siteNumber * site number to request RunFiles from * @param lastRunId @@ -3773,7 +3775,7 @@ private void sendRunFilesRequestToServer(int siteNumber, int lastRunId) { /** * Send run files to other site server. - * + * * @param siteNumber * @param lastRunId */ @@ -3787,7 +3789,7 @@ private void sendRunFilesToServer(int siteNumber, int lastRunId) { } private RunFiles[] getRunFiles(Run[] runs) { - + Vector runFilesList = new Vector(); for (Run run : runs) { @@ -3799,12 +3801,12 @@ private RunFiles[] getRunFiles(Run[] runs) { } } - return (RunFiles[]) runFilesList.toArray(new RunFiles[runFilesList.size()]); + return runFilesList.toArray(new RunFiles[runFilesList.size()]); } /** - * Returns local run list starting at run lastRunId. - * + * Returns local run list starting at run lastRunId. + * * @param lastRunId * @return */ @@ -3823,7 +3825,7 @@ private Run[] getLocalRunsStartingAt(int lastRunId) { } } - return (Run[]) listOfRuns.toArray(new Run[listOfRuns.size()]); + return listOfRuns.toArray(new Run[listOfRuns.size()]); } @@ -3889,10 +3891,10 @@ private void storeProfiles() { /** * Update sites in model. - * + * * There may be the case where more servers are defined * on a switch of a profile. - * + * * @param packet */ private void updateSitesToModel(Packet packet) { @@ -3906,12 +3908,12 @@ private void updateSitesToModel(Packet packet) { } } catch (Exception e) { controller.getLog().log(Log.WARNING, "Exception logged ", e); - } + } } /** * Login to all other servers. - * + * * @param packet * contains list of other servers */ @@ -3942,7 +3944,7 @@ private void loginToOtherSites(Packet packet) { /** * Return all accounts for all sites. - * + * * @return Array of all accounts in contest. */ private Account[] getAllAccounts() { @@ -3956,13 +3958,13 @@ private Account[] getAllAccounts() { } } - Account[] accountList = (Account[]) allAccounts.toArray(new Account[allAccounts.size()]); + Account[] accountList = allAccounts.toArray(new Account[allAccounts.size()]); return accountList; } /** * Return an array of all logged in users. - * + * * @return array of clientId's. */ private ClientId[] getAllRemoteLoggedInUsers() { @@ -3979,14 +3981,14 @@ private ClientId[] getAllRemoteLoggedInUsers() { if (clientList.size() == 0) { return new ClientId[0]; } else { - ClientId[] clients = (ClientId[]) clientList.toArray(new ClientId[clientList.size()]); + ClientId[] clients = clientList.toArray(new ClientId[clientList.size()]); return clients; } } /** * Return an array of all logged in users. - * + * * @return array of clientId's. */ private ClientId[] getAllLocalLoggedInUsers() { @@ -4003,7 +4005,7 @@ private ClientId[] getAllLocalLoggedInUsers() { if (clientList.size() == 0) { return new ClientId[0]; } else { - ClientId[] clients = (ClientId[]) clientList.toArray(new ClientId[clientList.size()]); + ClientId[] clients = clientList.toArray(new ClientId[clientList.size()]); return clients; } } @@ -4055,9 +4057,9 @@ public ContestLoginSuccessData createContestLoginSuccessData(IInternalContest in Profile profile = inContest.getProfile(); profiles = new Profile[1]; profiles[0] = profile; - + problems = getProblemsForTeam (inContest, clientId); - + } else { runs = inContest.getRuns(); clarifications = inContest.getClarifications(); @@ -4080,7 +4082,7 @@ public ContestLoginSuccessData createContestLoginSuccessData(IInternalContest in contestLoginSuccessData.setGroups(inContest.getGroups()); contestLoginSuccessData.setJudgements(inContest.getJudgements()); contestLoginSuccessData.setLanguages(inContest.getLanguages()); - + if (ClientType.Type.ADMINISTRATOR.equals(clientId.getClientType()) || ClientType.Type.SERVER.equals(clientId.getClientType()) ) { @@ -4110,7 +4112,7 @@ public ContestLoginSuccessData createContestLoginSuccessData(IInternalContest in if (isServer(clientId)) { contestLoginSuccessData.setContestSecurityPassword(contestSecurityPassword); } - + if (inContest.getCategories() != null){ contestLoginSuccessData.setCategories(inContest.getCategories()); } @@ -4120,7 +4122,7 @@ public ContestLoginSuccessData createContestLoginSuccessData(IInternalContest in /** * Get problems that only this team can use/view based on their group. - * + * * @param inContest * @param clientId * @return @@ -4129,58 +4131,40 @@ protected Problem[] getProblemsForTeam(IInternalContest inContest, ClientId clie Account account = inContest.getAccount(clientId); - ElementId groupId = account.getGroupId(); - Group group = null; - if (groupId != null) { - group = inContest.getGroup(groupId); - } + HashSet groups = account.getGroupIds(); - if (group == null) { - /** - * Not assigned a group, they get all problems. - */ - return inContest.getProblems(); - } + if(groups != null) { + ArrayList teamProblems = new ArrayList(); - Problem[] problems = inContest.getProblems(); + for(ElementId groupId : groups) { + Group group = inContest.getGroup(groupId); + Problem[] problems = inContest.getProblems(); - // count the problems that this team can view. - int count = 0; - for (Problem problem : problems) { - if (teamCanViewProblem(problem, group)) { - count++; - } - } - - if (count < problems.length) { - /** - * Team can only view some of the problems. - */ - Problem[] teamsProblems = new Problem[count]; - int i = 0; - for (Problem problem : problems) { - if (teamCanViewProblem(problem, group)) { - teamsProblems[i] = problem; - i++; + // count the problems that this team can view. + int count = 0; + for (Problem problem : problems) { + if (teamCanViewProblem(problem, group)) { + teamProblems.add(problem); + } } } - return teamsProblems; - } else { - /** - * Team can view all of the problems. - */ - return problems; + return(teamProblems.toArray(new Problem [0])); } + /** + * Not assigned a group, they get all problems. + */ + return inContest.getProblems(); + } - + private boolean teamCanViewProblem(Problem problem, Group group) { return problem.isAllView() || problem.canView(group); } /** * Create a login success packet. - * - * + * + * * @param clientId * @return Packet containing contest settings */ @@ -4192,20 +4176,20 @@ public Packet createLoginSuccessPacket(ClientId targetClientId, String contestSe return loginSuccessPacket; } - + /** * Got a status from the other server. * @param packet * @param connectionHandlerID */ private void handleServerStatus(Packet packet, ConnectionHandlerID connectionHandlerID) { - + Site site = (Site) PacketFactory.getObjectValue(packet, PacketFactory.SITE); Profile inProfile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE); Status status = (Status) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE_STATUS); - + contest.updateSiteStatus(site, inProfile, status); - + if (isServer()){ controller.sendToJudgesAndOthers(packet, false); } @@ -4213,20 +4197,20 @@ private void handleServerStatus(Packet packet, ConnectionHandlerID connectionHan /** * Handle incoming request on a server. - * + * * @param packet * @param connectionHandlerID */ private void handleRequestServerStatus(Packet packet, ConnectionHandlerID connectionHandlerID) { - + int targetSiteNumber = (Integer) PacketFactory.getObjectValue(packet, PacketFactory.SITE_NUMBER); ClientId remoteServerId = new ClientId(targetSiteNumber, ClientType.Type.SERVER, 0); - + if (isThisSite(targetSiteNumber)){ - + Profile expectedProfile = (Profile) PacketFactory.getObjectValue(packet, PacketFactory.PROFILE); sendStatusToServers(packet, expectedProfile); - + } else { /** * Send to target server. @@ -4239,26 +4223,26 @@ private void handleRequestServerStatus(Packet packet, ConnectionHandlerID connec /** * Send a status packe to all logged in servers. - * + * * @param packet * @param expectedProfile */ private void sendStatusToServers(Packet packet, Profile expectedProfile) { - + Status status = Status.NOTREADY; if (contest.getProfile().getProfilePath().equals(expectedProfile.getProfilePath())){ status = Status.READY_TO_SWITCH; } - + Site site = contest.getSite(contest.getSiteNumber()); Packet statusPacket = PacketFactory.createServerStatusPacket(getServerClientId(), PacketFactory.ALL_SERVERS, contest.getProfile(), status, site); - + controller.sendToServers(statusPacket); } /** * Is the input ClientId a server. - * + * * @param id * @return */ @@ -4272,7 +4256,7 @@ private boolean isJudge(ClientId id) { /** * Is this client a server. - * + * * @return true if is a server. */ private boolean isServer() { @@ -4285,25 +4269,25 @@ private void info(Exception e) { Utilities.debugPrint(e); } - + public void info(String s) { controller.getLog().info(s); Utilities.debugPrint(s); } - + private void handleShutdownAllServers(Packet packet, ConnectionHandlerID connectionHandlerID, ClientId fromId) { ClientId requestor = packet.getSourceId(); if (contest.isAllowed(requestor, Permission.Type.SHUTDOWN_ALL_SERVERS)) { - + controller.shutdownRemoteServers (requestor); - + // Sleep for a couple of seconds to allow time to send // shutdown packets to send to other sites. - + sleepSecs(2); - + controller.shutdownServer(packet.getSourceId()); } else { @@ -4349,7 +4333,7 @@ private void handleServerShutdown(Packet packet, ConnectionHandlerID connectionH /** * Send message to all servers. - * + * * @param area * @param message * @param ex diff --git a/src/edu/csus/ecs/pc2/core/archive/PacketFormatter.java b/src/edu/csus/ecs/pc2/core/archive/PacketFormatter.java index c702d7c87..b00fb0023 100644 --- a/src/edu/csus/ecs/pc2/core/archive/PacketFormatter.java +++ b/src/edu/csus/ecs/pc2/core/archive/PacketFormatter.java @@ -5,6 +5,7 @@ import java.util.Date; import java.util.Enumeration; import java.util.GregorianCalendar; +import java.util.HashSet; import java.util.Properties; import javax.swing.tree.DefaultMutableTreeNode; @@ -26,6 +27,7 @@ import edu.csus.ecs.pc2.core.model.ClientType; import edu.csus.ecs.pc2.core.model.ContestInformation; import edu.csus.ecs.pc2.core.model.ContestTime; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Filter; import edu.csus.ecs.pc2.core.model.FinalizeData; import edu.csus.ecs.pc2.core.model.Group; @@ -307,9 +309,20 @@ private static DefaultMutableTreeNode createTree(String key, Object object) { child = new DefaultMutableTreeNode(" Name: " + account.getDisplayName()); node.add(child); - - child = new DefaultMutableTreeNode(" Group: " + account.getGroupId()); - node.add(child); + + HashSet groups = account.getGroupIds(); + if(groups == null) { + child = new DefaultMutableTreeNode(" Group list: 0 Groups"); + node.add(child); + } else { + DefaultMutableTreeNode groupsnode = new DefaultMutableTreeNode(key + "Group list: " + groups.size() + " Groups"); + + for (ElementId elementId: groups) { + child = new DefaultMutableTreeNode(" Group list: " + elementId.toString()); + groupsnode.add(child); + } + node.add(groupsnode);; + } child = new DefaultMutableTreeNode(" Site: " + account.getSiteNumber()); node.add(child); diff --git a/src/edu/csus/ecs/pc2/core/imports/ContestXML.java b/src/edu/csus/ecs/pc2/core/imports/ContestXML.java index 337b75568..afd2e12a7 100644 --- a/src/edu/csus/ecs/pc2/core/imports/ContestXML.java +++ b/src/edu/csus/ecs/pc2/core/imports/ContestXML.java @@ -19,6 +19,7 @@ import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ContestInformation; import edu.csus.ecs.pc2.core.model.ContestTime; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Filter; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.JudgementRecord; @@ -447,8 +448,12 @@ public IMemento addAccountMemento(IMemento mementoRoot, IInternalContest contest IMemento accountMemento = mementoRoot.createChild(ACCOUNT_TAG); ClientId clientId = account.getClientId(); accountMemento.putString("name", account.getDisplayName()); - if (account.getGroupId() != null) { - accountMemento.putString("group", contest.getGroup(account.getGroupId()).toString()); + if (account.getGroupIds() != null) { + int gcnt = 1; + for(ElementId elementId: account.getGroupIds()) { + accountMemento.putString("group" + gcnt, contest.getGroup(elementId).toString()); + gcnt++; + } } accountMemento.putString("type", clientId.getClientType().toString()); diff --git a/src/edu/csus/ecs/pc2/core/imports/ExportAccounts.java b/src/edu/csus/ecs/pc2/core/imports/ExportAccounts.java index ee446bb49..a3f7c2498 100644 --- a/src/edu/csus/ecs/pc2/core/imports/ExportAccounts.java +++ b/src/edu/csus/ecs/pc2/core/imports/ExportAccounts.java @@ -1,4 +1,4 @@ -// 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.core.imports; import java.io.File; @@ -6,6 +6,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; @@ -23,7 +24,7 @@ /** * Provide a class that will save the accounts in various formats. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -35,11 +36,11 @@ public final class ExportAccounts { */ public static final String[] COLUMN_TITLES = { "site", "account", "displayname", "password", "group", "permdisplay", // "permlogin", "externalid", "alias", "permpassword", // - + Constants.LONGSCHOOLNAME_COLUMN_NAME, Constants.SHORTSCHOOLNAME_COLUMN_NAME, Constants.COUNTRY_CODE_COLUMN_NAME, - + }; - + private static Hashtable groupHash; private static Exception exception = null; @@ -49,7 +50,7 @@ private ExportAccounts() { /** * Output file formats. - * + * * @author pc2@ecs.csus.edu */ public enum Formats { @@ -69,8 +70,8 @@ public enum Formats { /** * Save accounts for filename based on format. - * - * @param outputFile + * + * @param outputFile * @return true on success, otherwise returns false and getException() can be used to get the error */ public static boolean saveAccounts(Formats format, Account[] accounts, Group[] groups, File outputFile) { @@ -172,8 +173,16 @@ public static String[] buildAccountString(Account account) { a[1] = account.getClientId().getName(); a[2] = account.getDisplayName(); a[3] = account.getPassword(); - if (account.getGroupId() != null) { - a[4] = groupHash.get(account.getGroupId()); + // for groups, we create a csv list of them + if (account.getGroupIds() != null) { + ArrayList groupnames = new ArrayList(); + for(ElementId elementId: account.getGroupIds()) { + String groupName = groupHash.get(elementId); + if(groupName != null) { + groupnames.add(groupName); + } + } + a[4] = String.join(",", groupnames); } else { a[4] = ""; //$NON-NLS-1$ } @@ -187,7 +196,7 @@ public static String[] buildAccountString(Account account) { a[10] = account.getLongSchoolName(); a[11] = account.getShortSchoolName(); a[12] = account.getCountryCode(); - + return a; } @@ -218,24 +227,29 @@ public static String buildXMLString(Account[] accounts, Group[] groups) { return xmlString; } - + public static void addSingleAccountXML(Account account, Hashtable groups, XMLMemento mementoRoot) { // titles = { "site", "account", "displayname", "password", "group", "permdisplay", "permlogin", "externalid", "alias", "permpassword" }; // XXX these 1st ones are from teamStanding of the scoreboard xml IMemento accountMemento = mementoRoot.createChild("account"); - accountMemento.putString("teamName", account.getDisplayName()); + accountMemento.putString("teamName", account.getDisplayName()); accountMemento.putInteger("teamId", account.getClientId().getClientNumber()); accountMemento.putInteger("teamSiteId", account.getClientId().getSiteNumber()); accountMemento.putString("teamKey", account.getClientId().getTripletKey()); accountMemento.putString("teamExternalId", account.getExternalId()); accountMemento.putString("teamAlias", account.getAliasName().trim()); // now the rest of the the data - String groupName = ""; - if (account.getGroupId() != null) { - groupName = groups.get(account.getGroupId()); + // for groups, we create a separate tag , and a list of under that + if (account.getGroupIds() != null) { + IMemento groupMemento = accountMemento.createChild("groups"); + for(ElementId elementId: account.getGroupIds()) { + String groupName = groupHash.get(elementId); + if(groupName != null) { + groupMemento.putString("group", groupName); + } + } } - accountMemento.putString("groupName", groupName); accountMemento.putString("accountName", account.getClientId().getName()); accountMemento.putInteger("siteId", account.getSiteNumber()); accountMemento.putString("password", account.getPassword()); diff --git a/src/edu/csus/ecs/pc2/core/imports/ICPCAccount.java b/src/edu/csus/ecs/pc2/core/imports/ICPCAccount.java index 07a56e2c3..34fe587a0 100644 --- a/src/edu/csus/ecs/pc2/core/imports/ICPCAccount.java +++ b/src/edu/csus/ecs/pc2/core/imports/ICPCAccount.java @@ -1,16 +1,18 @@ -// 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.core.imports; +import java.util.HashMap; + import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ElementId; /** * ICPC Account information. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -23,46 +25,55 @@ public class ICPCAccount { * An external identifier, ex: an ICPC id */ private String externalId = ""; - + /** * Group id */ - private ElementId groupId; - + private HashMap groups; + + /** + * Used for importing from an old teams.tab type file, there is one group + * specified in the tab file, and it is a CMS id. We store it here and it will + * get converted after the account is created and placed in groups above. + */ + private String externalGroupId; + private ClientId clientId; - - private String groupExternalId = ""; - + private String shortSchoolName = ""; private String longSchoolName = ""; private String externalName = ""; /** - * + * */ public ICPCAccount() { super(); } - public ICPCAccount(Account account, String inGroupExternalId) { + + /** + * Fill in an ICPCAccount. + * + * @param account The PC2 account + * @param groupHash Maps group element ids to CMS group id + */ + public ICPCAccount(Account account, HashMap groupHash) { accountNumber = account.getClientId().getClientNumber(); clientId = account.getClientId(); externalId = account.getExternalId(); externalName = account.getExternalName(); - if (inGroupExternalId != null) { - // need to convert this groupId to the externalId - groupExternalId = inGroupExternalId; + if(account.getGroupIds() != null && groupHash != null) { + for(ElementId elementId: account.getGroupIds()) { + groups.put(elementId, groupHash.get(elementId)); + } } - groupId = account.getGroupId(); longSchoolName = account.getLongSchoolName(); shortSchoolName = account.getShortSchoolName(); } public void setExternalId(String id) { externalId = id; } - public void setGroupExternalId(String groupExternalId) { - this.groupExternalId = groupExternalId; - } public void setExternalName(String name) { externalName = name; } @@ -96,12 +107,6 @@ public String getExternalId() { public String getExternalName() { return externalName; } - /** - * @return Returns the groupId. - */ - public String getGroupExternalId() { - return groupExternalId; - } /** * @return Returns the longSchoolName. */ @@ -127,23 +132,45 @@ public void setClientId(ClientId clientId) { this.clientId = clientId; } /** - * @return Returns the groupId. + * @return Returns the groups map. + */ + public HashMap getGroups() { + return groups; + } + + /** + * Add external group ID (CMS) to the group map. Currently, only one group is supported for the ICPCAccount + * since the CMS only provides one group. It is a HashMap now for future use. We only use one entry for now. + * + * @param elementId + * @param externalGroupId + */ + public void addGroupId(ElementId elementId, String externalGroupId) { + if(groups == null) { + groups = new HashMap(); + } + groups.put(elementId, externalGroupId); + } + + /** + * @param externalGroupId The CMS groupId to set after the account is created */ - public ElementId getGroupId() { - return groupId; + public void setExternalGroupId(String groupId) { + this.externalGroupId = groupId; } /** - * @param groupId The groupId to set. + * Returns externalGroupid specified in tab file. This is set to null AFTER an ICPCAccount is created. + * It is only a place holder used during account creation from a TAB file. */ - public void setGroupId(ElementId groupId) { - this.groupId = groupId; + public String getExternalGroupId() { + return(this.externalGroupId); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { - return accountNumber+" "+groupId+" "+externalName+ " " +shortSchoolName; + return accountNumber+" "+groups+" "+externalName+ " " +shortSchoolName; } } diff --git a/src/edu/csus/ecs/pc2/core/imports/ICPCImportData.java b/src/edu/csus/ecs/pc2/core/imports/ICPCImportData.java index 7ee97c260..51f7886d9 100644 --- a/src/edu/csus/ecs/pc2/core/imports/ICPCImportData.java +++ b/src/edu/csus/ecs/pc2/core/imports/ICPCImportData.java @@ -11,7 +11,7 @@ /** * ICPC Import Data. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -26,7 +26,7 @@ public class ICPCImportData { private ICPCAccount[] accounts; /** - * + * */ public ICPCImportData() { super(); @@ -43,8 +43,8 @@ public ICPCImportData(Vector inAccounts, Group[] inGroups, String inCon accounts = new ICPCAccount[inAccounts.size()]; int accountCount = 0; while (accountsEnum.hasMoreElements()) { - Account account = (Account) accountsEnum.nextElement(); - ICPCAccount icpcAccount = new ICPCAccount(account, groupHash.get(account.getGroupId())); + Account account = accountsEnum.nextElement(); + ICPCAccount icpcAccount = new ICPCAccount(account, groupHash); accounts[accountCount] = icpcAccount; accountCount++; } diff --git a/src/edu/csus/ecs/pc2/core/imports/LoadAccounts.java b/src/edu/csus/ecs/pc2/core/imports/LoadAccounts.java index 54eccc34d..ce71a3ec0 100644 --- a/src/edu/csus/ecs/pc2/core/imports/LoadAccounts.java +++ b/src/edu/csus/ecs/pc2/core/imports/LoadAccounts.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2022 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.core.imports; import java.io.BufferedReader; @@ -6,6 +6,7 @@ import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import edu.csus.ecs.pc2.core.Constants; @@ -17,6 +18,7 @@ import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; import edu.csus.ecs.pc2.core.model.ClientType.Type; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.security.Permission; @@ -25,16 +27,16 @@ /** * Methods that provide updated accounts for an input model and load account TSV file. - * + * * @author Troy pc2@ecs.csus.edu */ public class LoadAccounts { - + /** * List of existing groups */ private Map groups = new HashMap(); - + /** * List of existing Accounts */ @@ -53,11 +55,11 @@ public class LoadAccounts { private int permDisplayColumn = -1; private int permLoginColumn = -1; - + private int aliasColumn = -1; - + private int externalIdColumn = -1; - + private int permPasswordColumn = -1; /* @@ -69,9 +71,9 @@ public class LoadAccounts { private int teamNameColumn = -1; private int scoreAdjustmentColumn = -1; private int institutionCodeColumn = -1; - + /** - * + * */ public LoadAccounts() { super(); @@ -94,22 +96,28 @@ protected Account getAccount(String[] values) { // TODO SOMEDAY would be nice if Account had a deep clone Account account = new Account(accountClean.getClientId(), accountClean.getPassword(), accountClean.getClientId().getSiteNumber()); account.clearListAndLoadPermissions(accountClean.getPermissionList()); - account.setGroupId(accountClean.getGroupId()); account.setDisplayName(new String(accountClean.getDisplayName())); account.setAliasName(new String(accountClean.getAliasName())); account.setExternalId(new String(accountClean.getExternalId())); account.setExternalName(new String(accountClean.getExternalName())); - account.setGroupId(accountClean.getGroupId()); account.setLongSchoolName(new String(accountClean.getLongSchoolName())); account.setShortSchoolName(new String(accountClean.getShortSchoolName())); account.setInstitutionCode(new String(accountClean.getInstitutionCode())); account.setInstitutionName(new String(accountClean.getInstitutionName())); account.setInstitutionShortName(new String(accountClean.getInstitutionShortName())); + + // Copy groups to new account + HashSet groupsClean = accountClean.getGroupIds(); + if(groupsClean != null) { + for(ElementId elementId : groupsClean) { + account.addGroupId(elementId, elementId.equals(accountClean.getPrimaryGroupId())); + } + } String [] existingMembers = accountClean.getMemberNames(); if(existingMembers != null) { account.setMemberNames(StringUtilities.cloneStringArray(existingMembers)); } - + // now start changing if (passwordColumn != -1 && values.length > passwordColumn) { account.setPassword(values[passwordColumn]); @@ -135,11 +143,23 @@ protected Account getAccount(String[] values) { if (teamNameColumn != -1 && values.length > teamNameColumn) { account.setExternalName(values[teamNameColumn]); } - + if (groups.size() > 0) { if (groupColumn != -1 && values.length > groupColumn && values[groupColumn].length() > 0) { - if (groups.containsKey(values[groupColumn])) { - account.setGroupId(groups.get(values[groupColumn]).getElementId()); + boolean needPrimaryGroup = true; + + // only set primary group if it is not already set. + if(account.getPrimaryGroupId() != null) { + needPrimaryGroup = false; + } + + // may be a CSV list of CMS groups ids (to support multiple groups / team) + String [] cmsGroups = values[groupColumn].split(","); + for(String cmsGroup : cmsGroups) { + if (groups.containsKey(cmsGroup)) { + account.addGroupId(groups.get(cmsGroup).getElementId(), needPrimaryGroup); + needPrimaryGroup = false; + } } } } @@ -208,11 +228,11 @@ protected Account getAccount(String[] values) { } return account; } - - + + /** * Create a new account or update an existing account given input values from load accounts file. - * + * * @param values * @return * @throws IllegalTSVFormatException @@ -237,27 +257,34 @@ protected Account getAccountFromFields(String[] values) throws IllegalTSVFormatE throw new IllegalTSVFormatException("Password required for new account for " + clientId); } } - + Account account = new Account(existingAccount.getClientId(), existingAccount.getPassword(), existingAccount.getClientId().getSiteNumber()); - + account.clearListAndLoadPermissions(existingAccount.getPermissionList()); - account.setGroupId(existingAccount.getGroupId()); account.setDisplayName(new String(existingAccount.getDisplayName())); account.setAliasName(new String(existingAccount.getAliasName())); account.setExternalId(new String(existingAccount.getExternalId())); account.setExternalName(new String(existingAccount.getExternalName())); - account.setGroupId(existingAccount.getGroupId()); account.setLongSchoolName(new String(existingAccount.getLongSchoolName())); account.setShortSchoolName(new String(existingAccount.getShortSchoolName())); account.setInstitutionCode(existingAccount.getInstitutionCode()); account.setInstitutionName(existingAccount.getInstitutionName()); account.setInstitutionShortName(existingAccount.getInstitutionShortName()); + + // Copy groups to new account + HashSet groupsExisting = existingAccount.getGroupIds(); + if(groupsExisting != null) { + for(ElementId elementId : groupsExisting) { + account.addGroupId(elementId, elementId.equals(existingAccount.getPrimaryGroupId())); + } + } + String [] existingMembers = existingAccount.getMemberNames(); if(existingMembers != null) { account.setMemberNames(StringUtilities.cloneStringArray(existingMembers)); } // now start updating fields - + if (passwordColumn != -1 && values.length > passwordColumn) { account.setPassword(values[passwordColumn]); } @@ -282,11 +309,24 @@ protected Account getAccountFromFields(String[] values) throws IllegalTSVFormatE if (teamNameColumn != -1 && values.length > teamNameColumn) { account.setExternalName(values[teamNameColumn]); } - + + if (groups.size() > 0) { if (groupColumn != -1 && values.length > groupColumn && values[groupColumn].length() > 0) { - if (groups.containsKey(values[groupColumn])) { - account.setGroupId(groups.get(values[groupColumn]).getElementId()); + boolean needPrimaryGroup = true; + + // only set primary group if it is not already set. + if(account.getPrimaryGroupId() != null) { + needPrimaryGroup = false; + } + + // may be a CSV list of CMS groups ids (to support multiple groups / team) + String [] cmsGroups = values[groupColumn].split(","); + for(String cmsGroup : cmsGroups) { + if (groups.containsKey(cmsGroup)) { + account.addGroupId(groups.get(cmsGroup).getElementId(), needPrimaryGroup); + needPrimaryGroup = false; + } } } } @@ -357,11 +397,11 @@ protected Account getAccountFromFields(String[] values) throws IllegalTSVFormatE } /** * Returns a list of accounts updated from the input load accounts file. - * + * * @see #fromTSVFile(IInternalContest, String, Account[], Group[]) - * + * * @param contest - * @param filename updates model accounts from file + * @param filename updates model accounts from file * @return a list of accounts to update. * @throws Exception */ @@ -374,27 +414,27 @@ public static Account[] updateAccountsFromFile(IInternalContest contest, String // we need to get them now since we have the contest object and these are all static methods that deal // with institutions loadInstitutions(contest); - + Account[] updatedAccounts = new LoadAccounts().fromTSVFile(contest, filename, curAccounts, curGroups); return updatedAccounts; } - + /** * Read a tab-separated values from a file. - * + * * File must have a header of field names. The fields can appear in any order. - * + * * Reads TSV file, updates existing accounts in model, returns only a list * of accounts that should be updated. - * + * *

- * + * * All accounts that are specified in the tsv file must exist, if an account * does not exist then a IllegalTSVFormatException will be thrown. - * + * *

- * - * + * + * * 1st line should contain the column headers. Columns can appear in any order. * Supported column headers are: *

@@ -408,28 +448,28 @@ public static Account[] updateAccountsFromFile(IInternalContest contest, String
      * site (required)
 
      * 
- * + * * @param contest needed if institutions are to be used * @param filename - * @param existingAccounts + * @param existingAccounts * @param groupList * @return an array of accounts * @throws Exception */ public Account[] fromTSVFile(IInternalContest contest, String filename, Account[] existingAccounts, Group[] groupList) throws Exception { - + /** * Output accounts */ Map accountMap = new HashMap(); - + if (existingAccounts != null && existingAccounts.length > 0) { for (int i = 0; i < existingAccounts.length; i++) { existingAccountsMap.put(existingAccounts[i].getClientId(), existingAccounts[i]); } } createGroupMap(groupList); - + int lineCount = 0; String[] columns; BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "UTF8")); @@ -460,9 +500,9 @@ public Account[] fromTSVFile(IInternalContest contest, String filename, Account[ countryCodeColumn = -1; teamNameColumn = -1; institutionCodeColumn = -1; - + for (int i = 0; i < columns.length; i++) { - + if (Constants.SITE_COLUMN_NAME.equalsIgnoreCase(columns[i])) { siteColumn = i; } @@ -518,12 +558,12 @@ public Account[] fromTSVFile(IInternalContest contest, String filename, Account[ throw new IllegalTSVFormatException(msg); } } - + // only need to load institutions if the column is specified if(institutionCodeColumn != -1) { loadInstitutions(contest); } - + line = in.readLine(); lineCount++; while (line != null) { @@ -535,7 +575,7 @@ public Account[] fromTSVFile(IInternalContest contest, String filename, Account[ continue; } String[] values = TabSeparatedValueParser.parseLine(line); - + if (!values[accountColumn].equals("")) { // No such account in contest model Account account = getAccount(values); @@ -568,7 +608,7 @@ public Account[] fromTSVFile(IInternalContest contest, String filename, Account[ /** * Legacy routine for unit tests. Does not take an IInternalContest, meaning, institution codes will not work since we * have to validate them. - * + * * @param filename * @param existingAccounts * @param groupList @@ -578,12 +618,12 @@ public Account[] fromTSVFile(IInternalContest contest, String filename, Account[ public Account[] fromTSVFile(String filename, Account[] existingAccounts, Group[] groupList) throws Exception { return(fromTSVFile(null, filename, existingAccounts, groupList)); } - + /** * Update accounts from accounts load file. - * + * * @param loadFilename - accounts load filename. - * @throws Exception + * @throws Exception */ public static void updateAccountsFromLoadAccountsFile(IInternalContest contest, String loadAccountFilename) throws Exception { if (Utilities.fileExists(loadAccountFilename)) { @@ -592,33 +632,33 @@ public static void updateAccountsFromLoadAccountsFile(IInternalContest contest, // we need to get them now since we have the contest object and these are all static methods that deal // with institutions loadInstitutions(contest); - + Account[] updateAccounts = LoadAccounts.updateAccountsFromFile(contest, loadAccountFilename); contest.updateAccounts(updateAccounts); contest.storeConfiguration(StaticLog.getLog()); } } - - - + + + /** * Create a list of updated accounts, if account in TSV file does not exist will create a new Account. - * + * * Intention is a list of all accounts in the TSV files that need to be updated or added. - * + * * File must have a header of field names. The fields can appear in any order. - * + * * Reads TSV file, updates existing accounts in model, returns only a list * of accounts that should be updated. - * + * *

- * + * * All accounts that are specified in the tsv file must exist, if an account * does not exist then a IllegalTSVFormatException will be thrown. - * + * *

- * - * + * + * * 1st line should contain the column headers. Columns can appear in any order. * Supported column headers are: *

@@ -632,27 +672,27 @@ public static void updateAccountsFromLoadAccountsFile(IInternalContest contest,
      * site (required)
 
      * 
- * + * * @param filename - * @param existingAccounts + * @param existingAccounts * @param groupList * @return an array of accounts * @throws Exception */ public Account[] fromTSVFileWithNewAccounts(IInternalContest contest, String filename, Account[] existingAccounts, Group[] groupList) throws Exception { - + /** * Output accounts */ Map accountMap = new HashMap(); - + if (existingAccounts != null && existingAccounts.length > 0) { for (int i = 0; i < existingAccounts.length; i++) { existingAccountsMap.put(existingAccounts[i].getClientId(), existingAccounts[i]); } } createGroupMap(groupList); - + int lineCount = 0; String[] columns; BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "UTF8")); @@ -675,7 +715,7 @@ public Account[] fromTSVFileWithNewAccounts(IInternalContest contest, String fil permLoginColumn = -1; permPasswordColumn = -1; scoreAdjustmentColumn = -1; - + /* * These correspond to columns found in the icpc data */ @@ -684,9 +724,9 @@ public Account[] fromTSVFileWithNewAccounts(IInternalContest contest, String fil countryCodeColumn = -1; teamNameColumn = -1; institutionCodeColumn = -1; - + for (int i = 0; i < columns.length; i++) { - + if (Constants.SITE_COLUMN_NAME.equalsIgnoreCase(columns[i])) { siteColumn = i; } @@ -742,7 +782,7 @@ public Account[] fromTSVFileWithNewAccounts(IInternalContest contest, String fil throw new IllegalTSVFormatException(msg); } } - + // only load institutions if column is specified in file if(institutionCodeColumn != -1) { loadInstitutions(contest); @@ -760,10 +800,10 @@ public Account[] fromTSVFileWithNewAccounts(IInternalContest contest, String fil String[] values = TabSeparatedValueParser.parseLine(line); Account account = getAccountFromFields(values); - - + + accountMap.put(account.getClientId(), account); - + } catch (IllegalTSVFormatException e2) { // already a properly formatted exception in.close(); @@ -782,11 +822,11 @@ public Account[] fromTSVFileWithNewAccounts(IInternalContest contest, String fil in = null; return accountMap.values().toArray(new Account[accountMap.size()]); } - + /** * Creates a hash map of group ids and group display names to the group object * Used for mapping TSV group column value to a group - * + * * @param groupList List of groups used to populate the map * @throws NumberFormatException if getGroupId() does not return an int (should not happen) */ @@ -799,18 +839,18 @@ private void createGroupMap(Group[] groupList) //this should never throw an exception since getGroupId() returns 'int' //if it does, we want to abort the account load since something is really wrong. groups.put(Integer.toString(group.getGroupId()), group); - } + } } - + /** * Attempt to load the institutions from the supplied file. - * + * * @param file probable location of the institutions.tsv file * @return true if loaded, false if error (file not found, etc) */ public static boolean loadInstitutions(String file) { boolean found = false; - + try { // have to check existance of file since loadInstitutions() doesn't care if it exists or not if(new File(file).exists()) { @@ -822,10 +862,10 @@ public static boolean loadInstitutions(String file) { } return(found); } - + /** * Load the cdp institutions.tsv file, if present. This is so we can validate institution codes supplied. - * + * * @param contest - may be null, in which case we do not load institutions, and any attempt to change/set a new institution code will fail later */ public static void loadInstitutions(IInternalContest contest) { @@ -842,7 +882,7 @@ public static void loadInstitutions(IInternalContest contest) { /** * Set an account's institution (school) information (formal and normal names based on institution ID. - * + * * @param account The account whose institution is to be set * @param instCode Institution code, eg. 1474, INST-1474 or INST-U-1474 (all work - but * what a mess. Someone has to decide what an institution code IS. @@ -866,6 +906,6 @@ private static void setInstitutionInformation(Account account, String instCode) StaticLog.warning(message); System.out.println("WARNING: " + message); } - + } } diff --git a/src/edu/csus/ecs/pc2/core/imports/LoadICPCData.java b/src/edu/csus/ecs/pc2/core/imports/LoadICPCData.java index 710a86448..6272f8987 100644 --- a/src/edu/csus/ecs/pc2/core/imports/LoadICPCData.java +++ b/src/edu/csus/ecs/pc2/core/imports/LoadICPCData.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.imports; import java.io.BufferedReader; @@ -20,7 +20,7 @@ /** * Load ICPC CSV Data. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -35,7 +35,7 @@ public final class LoadICPCData { /** * Caller is responsible for merging groups from ICPCImportData with the model grouplist and updating the ContestTitle. - * + * * @param directory * @param sites * @return ICPCImportData populated with contestTitle and groups @@ -70,7 +70,7 @@ public static ICPCImportData loadSites(String directory, Site[] sites) throws Ex /** * This will load the PC2_Team.tab, including populating ICPCAccount group and client (if sufficient data is provided). - * + * * @param directory * @param groups * @param existingAccounts @@ -79,6 +79,7 @@ public static ICPCImportData loadSites(String directory, Site[] sites) throws Ex */ public static ICPCImportData loadAccounts(String directory, Group[] groups, Account[] existingAccounts) throws Exception { ICPCImportData accountData = new ICPCImportData(); + // Map of CMS Group ID's to PC2 Groups HashMap groupMap = new HashMap(); if (groups != null) { for (Group group : groups) { @@ -108,10 +109,15 @@ public static ICPCImportData loadAccounts(String directory, Group[] groups, Acco ICPCAccount[] accounts = accountData.getAccounts(); if (accounts != null && accounts.length > 0) { for (ICPCAccount account : accounts) { - if (account.getGroupExternalId().length() > 0) { - if (groupMap.containsKey(account.getGroupExternalId())) { - Group group = groupMap.get(account.getGroupExternalId()); - account.setGroupId(group.getElementId()); + // the external CMS group is was squirreled away when we processed the tab file in readFile above + // It needs to be converted to an ElementId and added to the groups map for the account + String extGroupId = account.getExternalGroupId(); + if (extGroupId != null && extGroupId.length() > 0) { + if (groupMap.containsKey(extGroupId)) { + Group group = groupMap.get(account.getExternalGroupId()); + account.addGroupId(group.getElementId(), extGroupId); + // external group id string is no longer valid since we only support the hashmap + account.setExternalGroupId(null); if (group.getSite() != null) { int siteNum = group.getSite().getSiteNumber(); int accountNum = account.getAccountNumber(); @@ -196,7 +202,7 @@ static void readFile(String filename, int fileType, Site[] sites, ICPCImportData /** * This processes a PC2_Team.tab entry. - * + * * @param values * @return file populated ICPCAccount */ @@ -227,10 +233,11 @@ private static ICPCAccount processTeam(String[] values) { account.setAccountNumber(clientNumber); } account.setExternalId(values[1 + offset]); + String groupId = values[2 + offset]; if (groupId != null && groupId.trim().length() > 0) { - account.setGroupExternalId(groupId.trim()); + account.setExternalGroupId(groupId.trim()); } account.setExternalName(values[4 + offset]); account.setLongSchoolName(values[5 + offset]); @@ -240,7 +247,7 @@ private static ICPCAccount processTeam(String[] values) { /** * This processes a PC2_Site.tab entry, if their are 9 fields it will associate the Group to a Site based on the referenced pc2 site number. - * + * * @param values * @param sites * @return populated (pc2) Group @@ -270,7 +277,7 @@ private static Group processSite(String[] values, Site[] sites) { /** * This will process the PC2_Contest.tab line to pull out the title. - * + * * @param values * @return InternalContest Title */ @@ -283,7 +290,7 @@ private static String processContest(String[] values) { } /** - * + * */ private LoadICPCData() { super(); diff --git a/src/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilities.java b/src/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilities.java index 87b83ac8f..3e542fa62 100644 --- a/src/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilities.java +++ b/src/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilities.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 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.core.imports.clics; import java.io.FileOutputStream; @@ -43,7 +43,7 @@ /** * A set of methods for CLICS API JSON - * + * * @author Douglas A. Lane * */ @@ -65,19 +65,19 @@ public class CLICSAwardUtilities { // {"id":"group-winner-4","citation":"Winner(s) of group Eindhoven University of Technology","team_ids":["28"]}, -// "citation": "Winner(s) of group Delft University of Technology", -// "citation": "Winner(s) of group Eindhoven University of Technology", +// "citation": "Winner(s) of group Delft University of Technology", +// "citation": "Winner(s) of group Eindhoven University of Technology", // // -// "citation": "First to solve problem crashingcompetitioncomputer", -// "citation": "First to solve problem grindinggravel", -// "citation": "First to solve problem housenumbering", +// "citation": "First to solve problem crashingcompetitioncomputer", +// "citation": "First to solve problem grindinggravel", +// "citation": "First to solve problem housenumbering", // -// "citation": "Contest winner", +// "citation": "Contest winner", /** * Return a list of CLICS Awards for a contest. - * + * * @param contest * @return * @throws IOException @@ -117,32 +117,32 @@ public static void addGroupWinners(IInternalContest contest, Run[] runs, List groupWinners = new HashMap(); - + try { ContestStandings contestStandings = ScoreboardUtilities.createContestStandings(contest); List teamStands = contestStandings.getTeamStandings(); for (TeamStanding teamStanding : teamStands) { ClientId clientId = createClientId(teamStanding); - Group teamGroup = getGroupForTeam(contest, clientId); - if (teamGroup != null) { - + + List teamGroups = getGroupsForTeam(contest, clientId); + for(Group teamGroup : teamGroups) { /** * Find group winner (or foundClientId will be null if no winner found yet) */ ClientId foundClientId = groupWinners.get(teamGroup); - + if (foundClientId == null && isActive(contest,clientId)) { - // found no group winner so if thie team is active it is the group winner - + // found no group winner so if this team is active it is the group winner + if (!"0".equals(teamStanding.getSolved())) { /** - * Team has to solve at least one problem to be the group winner + * Team has to solve at least one problem to be the group winner */ groupWinners.put(teamGroup, clientId); } @@ -159,7 +159,7 @@ public static void addGroupWinners(IInternalContest contest, Run[] runs, List getGroupsForTeam(IInternalContest contest, ClientId submitter) { + ArrayList groups = new ArrayList(); Account account = contest.getAccount(submitter); if (account != null) { - ElementId groupElementId = account.getGroupId(); - if (groupElementId != null) { - Group group = contest.getGroup(groupElementId); - if (group != null) { - return group; + if(account.getGroupIds() != null) { + for(ElementId groupElementId : account.getGroupIds()) { + Group group = contest.getGroup(groupElementId); + if (group != null) { + groups.add(group); + } } } } - return null; + return groups; } public static void addMedals(IInternalContest contest, List list) throws JsonParseException, JsonMappingException, JAXBException, IllegalContestState, IOException { @@ -218,7 +220,7 @@ public static void addMedals(IInternalContest contest, List list) th // for (TeamScoreRow teamScoreRow : rows) { // System.out.println("debug srow "+getStandingsRow(teamScoreRow)); // } - + int lastRankGolds = 4; int lastRankSilver = 8; int lastRankBronze = 12; @@ -231,7 +233,7 @@ public static void addMedals(IInternalContest contest, List list) th lastRankBronze = finalizeData.getBronzeRank(); } } - + TeamScoreRow teamRow = model.getRows().get(0); if (teamRow.getScore().getNum_solved() > 0) { @@ -240,7 +242,7 @@ public static void addMedals(IInternalContest contest, List list) th List scoreRows = model.getRows(); String[] teams = getTeamIdsByRankWhereSolved(scoreRows, 0, lastRankGolds, 1); - + if (teams.length > 0) { // "citation": "Gold medal winner", // "id": "gold-medal" @@ -261,8 +263,8 @@ public static void addMedals(IInternalContest contest, List list) th } } } - - + + /** * Get all teams ids between lowRank and rankInclHigh (inclusive) * @param scoreRows @@ -296,13 +298,13 @@ public static String[] getTeamIdsByRankWhereSolved(List scoreRows, } } - return (String[]) teamIds.toArray(new String[teamIds.size()]); + return teamIds.toArray(new String[teamIds.size()]); } /** * Add winner award. - * + * * @param contest * @param list * @throws JsonParseException @@ -342,7 +344,7 @@ public static void addWinner(IInternalContest contest, List list) th /** * Add first to solve awards to list - * + * * @param contest * @param runs * @param runs @@ -398,7 +400,7 @@ static String getStandingsRow(TeamScoreRow row) { /** * Load list of CLICS awards json from file. - * + * * @param filename * @return * @throws IOException @@ -420,7 +422,7 @@ public static List readAwardsList(String filename) throws IOExceptio /** * Get an object mapper that ignores unknown properties. - * + * * @return an object mapper that ignores unknown properties */ public static final ObjectMapper getMapper() { @@ -436,7 +438,7 @@ public static final ObjectMapper getMapper() { /** * Writes awards elements to file. - * + * * @param filename * @param awards * @return numnber of award elements written @@ -449,7 +451,7 @@ public static int writeAwardsJSONFile(String filename, List awards) /** * Writes awards elements to printWriter - * + * * @param printWriter * @param awards * @return number of award elements written diff --git a/src/edu/csus/ecs/pc2/core/model/Account.java b/src/edu/csus/ecs/pc2/core/model/Account.java index 08f2f240a..6f5089565 100644 --- a/src/edu/csus/ecs/pc2/core/model/Account.java +++ b/src/edu/csus/ecs/pc2/core/model/Account.java @@ -1,6 +1,8 @@ -// 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.core.model; +import java.util.HashSet; + import edu.csus.ecs.pc2.core.Constants; import edu.csus.ecs.pc2.core.StringUtilities; import edu.csus.ecs.pc2.core.list.AccountList; @@ -50,9 +52,10 @@ public class Account implements IElementObject { private String externalId = ""; /** - * Group id + * Group ids */ - private ElementId groupId; + private HashSet groupIds; + private ElementId primaryGroupId; private PermissionList permissionList = new PermissionList(); @@ -211,13 +214,13 @@ public boolean isSameAs(Account account) { if (!getClientId().equals(account.getClientId())) { return false; } - if (groupId == null || account.getGroupId() == null) { + if (groupIds == null || account.getGroupIds() == null) { // if only 1 is null then return false - if (!(groupId == null && account.getGroupId() == null)) { + if (!(groupIds == null && account.getGroupIds() == null)) { return false; } } else { - if (!groupId.equals(account.getGroupId())) { + if (!groupIds.equals(account.getGroupIds())) { return false; } } @@ -316,14 +319,32 @@ public void setExternalId(String externalId) { } /** - * Group element id. + * Group element ids */ - public ElementId getGroupId() { - return groupId; + public HashSet getGroupIds() { + return groupIds; + } + public boolean isGroupMember(ElementId element) { + return(groupIds != null && groupIds.contains(element)); } - public void setGroupId(ElementId groupId) { - this.groupId = groupId; + public void clearGroups() { + groupIds = null; + primaryGroupId = null; + } + + public void addGroupId(ElementId groupId, boolean isPrimary) { + if(groupIds == null) { + groupIds = new HashSet(); + } + if(isPrimary) { + primaryGroupId = groupId; + } + groupIds.add(groupId); + } + + public ElementId getPrimaryGroupId() { + return primaryGroupId; } public void setShortSchoolName(String shortSchoolName) { @@ -391,13 +412,21 @@ public void setMemberNames(String[] names) { * * @param account */ + @SuppressWarnings("unchecked") public void updateFrom(Account account) { aliasName = account.aliasName; countryCode = account.countryCode; displayName = account.displayName; externalId = account.externalId; externalName = account.externalName; - groupId = account.getGroupId(); + if(account.getGroupIds() != null) { + // shallow clone is OK, ElementId's don't change + groupIds = (HashSet) account.getGroupIds().clone(); + primaryGroupId = account.primaryGroupId; + } else { + groupIds = null; + primaryGroupId = null; + } longSchoolName = account.longSchoolName; password = account.password; shortSchoolName = account.shortSchoolName; diff --git a/src/edu/csus/ecs/pc2/core/model/Filter.java b/src/edu/csus/ecs/pc2/core/model/Filter.java index 877615755..3b74f3146 100644 --- a/src/edu/csus/ecs/pc2/core/model/Filter.java +++ b/src/edu/csus/ecs/pc2/core/model/Filter.java @@ -1,12 +1,17 @@ -// 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.core.model; import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.Enumeration; +import java.util.HashSet; import java.util.Hashtable; import java.util.Vector; +import edu.csus.ecs.pc2.core.IInternalController; +import edu.csus.ecs.pc2.core.log.Log; import edu.csus.ecs.pc2.core.model.Clarification.ClarificationStates; import edu.csus.ecs.pc2.core.model.ClientType.Type; import edu.csus.ecs.pc2.core.model.Run.RunStates; @@ -14,33 +19,33 @@ /** * A filter for runs, clarifications by site, clients, problems, languages. - * + * * Provides a way to determine whether a run, clarification, etc. matches a list of problems, sites, etc.
*

* An example of filter to print count of matching the filter. - * + * *

  * IInternalContest contest;
- * 
+ *
  * Problem problem = contest.getProblems()[0];
- * 
+ *
  * Run[] runs = contest.getRuns();
- * 
+ *
  * Filter filter = new Filter();
  * filter.addProblem(problem);
- * 
+ *
  * System.out.println("Count of filtered runs : " + filter.countRuns(runs));
  * System.out.println("Count of all runs      : " + runs.length);
- * 
+ *
  * 
- * + * * A newly constructed instance of Filter always returns true for all matches methods. In this way the filter matches, like {@link #matches(Run)}, method can be used/coded * unconditionally and match all {@link edu.csus.ecs.pc2.core.model.Run}s, then when a criteria is added (via the Filter add methods) the runs will be filtered/matched appropriately. *

- * + * * This example shows how to use the filter unconditionally, if there are no criteria in the filter then all Runs will be processed. If there are criteria only runs matching the filter will be * processed. - * + * *

  * for (Run run : runs) {
  *     if (filter.matches(run)) {
@@ -48,7 +53,7 @@
  *     }
  * }
  * 
- * + * * Individual classes (no pun) of criteria can be turned on and off via the setUsing methods, for example {@link #setUsingSitesFilter(boolean)}, if set to false, then all site * criteria in the filter will be ignored. *

@@ -58,19 +63,19 @@ * There are count methods that can be used to determine how many items match the Filter criteria. To learn the number of matching accounts, one can use the * {@link #countAccounts(Account[])} method, there other count methods {@link #countRuns(Run[])} to count {@link edu.csus.ecs.pc2.core.model.Run}s. *

- * + * * Each criterial class, there are a number of methods to add, remove, clar and turn of the filter for that class. - *

  • {@link #addSite(Site)} - add a site to the filter and activates site filter + *
  • {@link #addSite(Site)} - add a site to the filter and activates site filter *
  • {@link #addSite(int)} - add a site by site number to the filter and activates site filter *
  • {@link #clearSiteList()} - clear all site filter and turn off site filter *
  • {@link #removeSite(Site)} - remove a site from the filter *
  • {@link #setUsingSitesFilter(boolean)} to determine if site filter is on use {@link #isFilteringSites()} - *

    - * + *

    + * * Here is a list of methods that use the filter on a class: *

  • {@link #countRuns(Run[])} - count runs matching this Filter instance. *
  • {@link #matches(Run)} - return true if criteria matches the input {@link edu.csus.ecs.pc2.core.model.Run} - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -78,13 +83,18 @@ // $HeadURL$ public class Filter implements Serializable { - // TODO filter for permissions - /** - * + * */ private static final long serialVersionUID = -8373119928926075959L; + + private IInternalContest contest; + + private IInternalController controller; + + private Log log; + /** * collection of chosen run states */ @@ -120,30 +130,30 @@ public class Filter implements Serializable { * filtering on problem (problem id) */ private boolean filteringProblems = false; - + /** - * collection of problem ids + * collection of permission types */ private Hashtable permissionsHash = new Hashtable(); - + private boolean filteringPermissions = false; /** * collection of language ids */ private Hashtable languageIdHash = new Hashtable(); - + /** * Collection of site ids. */ private Hashtable siteIdHash = new Hashtable(); - + /** * filtering on language (language id) */ private boolean filteringLanguages = false; - + /** * filtering on site (site number) */ @@ -169,6 +179,10 @@ public class Filter implements Serializable { private boolean filteringAccounts = false; + private HashSet groupIdHash = new HashSet(); + + private boolean filteringGroups = false; + /** * filtering for this site only */ @@ -180,6 +194,12 @@ public class Filter implements Serializable { private boolean filteringDeleted = false; + public void setContestAndController(IInternalContest inContest, IInternalController inController) { + this.contest = inContest; + this.controller = inController; + log = controller.getLog(); + } + private ElementId getJudgementId(Run run) { JudgementRecord judgementRecord = run.getJudgementRecord(); if (judgementRecord != null) { @@ -187,10 +207,10 @@ private ElementId getJudgementId(Run run) { } return null; } - + /** * Returns true ("matching") if Run matches filter criteria. - * + * * @param run * @return true if the Run maches the filter, false otherwise. */ @@ -222,13 +242,13 @@ public boolean isFilteringDeleted() { /** * Match criteria against a clar. - * + * * @param clarification * @return true if the clarifications matches the filter */ public boolean matches(Clarification clarification) { if (filterEnabled){ - return matchesSites(clarification) && matchesAccount(clarification.getSubmitter()) && matchesClarificationState(clarification.getState()) + return matchesSites(clarification) && matchesAccount(clarification.getSubmitter()) && matchesClarificationState(clarification.getState()) && matchesProblem(clarification.getProblemId()) && matchesLanguage(clarification.getLanguageId()) && matchesElapsedTimeSubmission(clarification); } else { @@ -238,7 +258,7 @@ public boolean matches(Clarification clarification) { /** * Match criteria against a clientId. - * + * * @param clientId * @return true if the sites match */ @@ -281,7 +301,7 @@ public void setUsingJudgementFilter(boolean turnOn) { /** * Is filtering using judgement list. - * + * * @return true if filter judgements. */ public boolean isFilteringJudgements() { @@ -290,7 +310,7 @@ public boolean isFilteringJudgements() { /** * Add a judgement to match against. - * + * * @param judgement */ public void addJudgement(Judgement judgement) { @@ -299,9 +319,9 @@ public void addJudgement(Judgement judgement) { /** * Add a judgement to match against. - * + * * Also turns filtering on for judgement list. - * + * * @param elementId */ private void addJudgement(ElementId elementId) { @@ -311,24 +331,25 @@ private void addJudgement(ElementId elementId) { /** * Return true if judgement filter ON and matches a judgement in the filter list. - * + * * @param judgement */ public boolean matches(Judgement judgement) { return matchesJudgement(judgement.getElementId()); } /** - * + * * @param judgement * @deprecated use {@link #matches(Judgement)} */ + @Deprecated public boolean matchesJudgement(Judgement judgement) { return matchesJudgement(judgement.getElementId()); } /** * Return true if judgement filter ON and matches a judgement in the filter list. - * + * * @param judgementId */ public boolean matchesJudgement(ElementId judgementId) { @@ -351,7 +372,7 @@ public void setUsingProblemFilter(boolean turnOn) { /** * Is filtering using problem list. - * + * * @return true if filter problems. */ public boolean isFilteringProblems() { @@ -360,7 +381,7 @@ public boolean isFilteringProblems() { /** * Add a problem to match against. - * + * * @param problem */ public void addProblem(Problem problem) { @@ -369,19 +390,19 @@ public void addProblem(Problem problem) { /** * Add a problem to match against. - * + * * Also turns filtering on for problem list. - * + * * @param elementId */ private void addProblem(ElementId elementId) { problemIdHash.put(elementId, new Date()); filteringProblems = true; } - + /** * Add a permission type to match against. - * + * * @param type */ public void addPermission(Permission.Type type) { @@ -391,31 +412,49 @@ public void addPermission(Permission.Type type) { /** * Return true if problem filter ON and matches a problem in the filter list. - * + * * @param problem * @deprecated use {@link #matches(Problem)} */ + @Deprecated public boolean matchesProblem(Problem problem) { return matches(problem); } - + public boolean matches(Problem problem) { return matchesProblem(problem.getElementId()); } /** * Return true if problem filter ON and matches a problem in the filter list. - * + * * @param problemId */ public boolean matchesProblem(ElementId problemId) { + boolean match = true; if (filteringProblems) { - return problemIdHash.containsKey(problemId); - } else { - return true; + match = problemIdHash.containsKey(problemId); + if(!match) { + return(false); + } } + if(filteringGroups) { + if(contest != null) { + Problem problem = contest.getProblem(problemId); + if(problem != null) { + match = false; + for(ElementId groupElementId : groupIdHash) { + if(problem.canView(contest.getGroup(groupElementId))) { + match = true; + break; + } + } + } + } + } + return(match); } - + public boolean matches(Permission.Type type) { if (filteringPermissions) { return permissionsHash.containsKey(type); @@ -423,8 +462,8 @@ public boolean matches(Permission.Type type) { return true; } } - - + + public void setUsingPermissionFilter (boolean turnOn){ filteringPermissions = turnOn; } @@ -435,14 +474,14 @@ public void setUsingLanguageFilter(boolean turnOn) { /** * Turn the sites filter on or off. - * + * * This does not clear the sites filter information, * the {@link #matches(Run)} and other matches * methods will ignore the sites criteria. *

    * The {@link #addSite(int)} and {@link #addSite(Site)} will * effectively invoke a setUsingSitesFilter(true). - * + * * @param turnOn true means ignore site criteria */ public void setUsingSitesFilter(boolean turnOn) { @@ -452,19 +491,36 @@ public void setUsingSitesFilter(boolean turnOn) { public boolean isFilteringSites() { return filteringSites; } - + + /** + * Enable or disable the filtering based on groups + * @param turnOn + */ + public void setUsingGroupsFilter(boolean turnOn) { + filteringGroups = turnOn; + } + + /** + * Return indicating if filtering on groups is desired + * + * @return true if filtering by groups, false otherwise + */ + public boolean isFilteringGroups() { + return filteringGroups; + } + /** * Is filtering using permissions list. - * + * * @return true if filter languages. */ public boolean isFilteringPermissions() { return filteringPermissions; } - + /** * Is filtering using language list. - * + * * @return true if filter languages. */ public boolean isFilteringLanguages() { @@ -473,7 +529,7 @@ public boolean isFilteringLanguages() { /** * Add a language to match against. - * + * * @param language */ public void addLanguage(Language language) { @@ -482,9 +538,9 @@ public void addLanguage(Language language) { /** * Add a language to match against. - * + * * Also turns filtering on for language list. - * + * * @param elementId */ private void addLanguage(ElementId elementId) { @@ -494,22 +550,23 @@ private void addLanguage(ElementId elementId) { /** * Return true if language filter ON and matches a language in the filter list. - * + * * @param language * @deprecated use {@link #matches(Language)} */ - + + @Deprecated public boolean matchesLanguage(Language language) { return matchesLanguage(language.getElementId()); } - + public boolean matches(Language language) { return matchesLanguage(language.getElementId()); } /** * Return true if language filter ON and matches a language in the filter list. - * + * * @param languageId */ public boolean matchesLanguage(ElementId languageId) { @@ -522,7 +579,7 @@ public boolean matchesLanguage(ElementId languageId) { /** * Add a site to match against. - * + * * @param site */ public void addSite(Site site) { @@ -531,16 +588,16 @@ public void addSite(Site site) { /** * Add a site to match against. - * + * * Also turns filtering on for site list. - * + * * @param siteNumber */ public void addSite(int siteNumber) { siteIdHash.put(new Integer(siteNumber), new Date()); filteringSites = true; } - + /** * Returns true if submission matches sites filter. * @param submission a run or clarification @@ -561,41 +618,102 @@ private boolean matchesSites(int siteNumber) { /** * Return true if site filter ON and matches a site in the filter list. - * + * * @param site * @deprecated use {@link #matches(Site)} */ + @Deprecated public boolean matchesSite(Site site) { return matches(site); } - + public boolean matches(Site site) { return matchesSites(site.getSiteNumber()); } /** - * + * Add a group to match against. + * + * Also turns filtering on for groups hashset. + * + * @param group the group to add to the filter + */ + public void addGroup(Group group) { + if(group != null) { + addGroup(group.getElementId()); + } + } + + /** + * Add a group element to match against. + * + * Also turns filtering on for groups hashset. + * + * @param groupElementId + */ + public void addGroup(ElementId groupElementId) { + groupIdHash.add(groupElementId); + filteringGroups = true; + } + + private boolean matchesGroups(ElementId groupElementId) { + if(filteringGroups) { + return groupIdHash.contains(groupElementId); + } + return(true); + } + + /** + * Determine if any groups in the supplied set are groups in the filter. + * + * @param groups to check + * @return true if there is any group in groups that's allowed by the filter, false otherwise + */ + public boolean matches(Collection groups) { + if(filteringGroups && groups != null) { + return !Collections.disjoint(groups, groupIdHash); + } + return(true); + } + + /** + * Determine if the supplied group is allowed by the filter + * + * @param group + * @return true if the group is allowed, false otherwise + */ + public boolean matches(Group group) { + return matchesGroups(group.getElementId()); + } + + /** + * * @param account * @return * @deprecated use {@link #matches(Account)} */ + @Deprecated public boolean matchesAccount(Account account) { return matches(account); } - - + + public boolean matches(Account account) { - + ClientId clientId = account.getClientId(); - + if (! matchesSites(clientId.getSiteNumber())) { return false; } - + + if (! matches(account.getGroupIds())) { + return false; + } + if (! matchesPermission(account)) { return false; } - + if (filteringAccounts) { if (matches(clientId)) { return matches(clientId.getClientType()); @@ -605,13 +723,13 @@ public boolean matches(Account account) { return true; } } - + public Permission.Type[] getPermissionsList() { Permission.Type[] permList = new Permission.Type[permissionsHash.size()]; Enumeration enumeration = permissionsHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - Permission.Type element = (Permission.Type) enumeration.nextElement(); + Permission.Type element = enumeration.nextElement(); permList[i] = element; i++; } @@ -619,12 +737,12 @@ public Permission.Type[] getPermissionsList() { } private boolean matchesPermission(Account account) { - + if (filteringPermissions) { Enumeration enumeration = permissionsHash.keys(); while (enumeration.hasMoreElements()) { - Permission.Type type = (Permission.Type) enumeration.nextElement(); + Permission.Type type = enumeration.nextElement(); if (!account.isAllowed(type)) { return false; } @@ -656,28 +774,32 @@ public boolean matches(Type type) { /** * Client Id matches both account filter and sites filter. - * + * * @param clientId * @return true if matches filter criteria */ public boolean matchesAccount(ClientId clientId) { + if(!matchesSites(clientId)) { + return false; + } if (filteringAccounts) { // System.out.println(new FilterFormatter().getClientsShortList(getAccountList())); - - if (matchesSites(clientId)) { - if (clientIdHash.size() == 0) { - return true; - } else { - return clientIdHash.containsKey(clientId); - } - } else { + + if (clientIdHash.size() != 0 && !clientIdHash.containsKey(clientId)) { return false; } - } else { - return matchesSites(clientId); } + if(filteringGroups) { + if(contest != null) { + Account account = contest.getAccount(clientId); + if(account != null && !matches(account.getGroupIds())) { + return false; + } + } + } + return true; } - + /** * Clear judgements and turn judgement filtering off. * @@ -697,7 +819,7 @@ public void clearAccountList() { filteringAccounts = false; clientIdHash = new Hashtable(); } - + public void clearClientTypesList() { clientTypeHash = new Hashtable(); } @@ -710,11 +832,11 @@ public void clearProblemList() { filteringProblems = false; problemIdHash = new Hashtable(); } - + public void clearPermissionsList() { permissionsHash = new Hashtable(); } - + /** * Clear language and turn language filtering off. * @@ -723,7 +845,7 @@ public void clearLanguageList() { filteringLanguages = false; languageIdHash = new Hashtable(); } - + /** * Clear site and turn site filtering off. * @@ -733,6 +855,14 @@ public void clearSiteList() { siteIdHash = new Hashtable(); } + /** + * Clear groups filter and turn of group filtering + */ + public void clearGroupsList() { + filteringGroups = false; + groupIdHash.clear(); + } + /** * Remove the input site from the site filter. * @param site @@ -740,7 +870,7 @@ public void clearSiteList() { public void removeSite(Site site) { if (siteIdHash.containsKey(site.getSiteNumber())) { siteIdHash.remove(site.getSiteNumber()); - // TODO add setUsingSitesFilter(false); + // TODO add setUsingSitesFilter(false); } } @@ -751,10 +881,10 @@ public void removeSite(Site site) { public void removeProblem(Problem problem) { if (problemIdHash.containsKey(problem.getElementId())) { problemIdHash.remove(problem.getElementId()); - // TODO add setUsingProblemFilter(false); + // TODO add setUsingProblemFilter(false); } } - + /** * Remove permission from the permission filter. * @param permission @@ -764,10 +894,10 @@ public void removePermission(Permission.Type type) { permissionsHash.remove(type); } } - + /** * Remove language from the language filter. - * + * * @param language */ public void removeLanguage(Language language) { @@ -776,10 +906,16 @@ public void removeLanguage(Language language) { } } - + public void removeGroup(Group group) { + groupIdHash.remove(group.getElementId()); + if(filteringGroups && groupIdHash.isEmpty()) { + filteringGroups = false; + } + } + /** * Get list of ElementIds for the judgement in the filter list. - * + * * @return list of element ids. */ public ElementId[] getJudgementIdList() { @@ -787,17 +923,17 @@ public ElementId[] getJudgementIdList() { Enumeration enumeration = judgementIdHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - ElementId element = (ElementId) enumeration.nextElement(); + ElementId element = enumeration.nextElement(); elementIds[i] = element; i++; } return elementIds; } - + /** * Get list of ElementIds for the problems in the filter list. - * + * * @return list of element ids. */ public ElementId[] getProblemIdList() { @@ -805,16 +941,16 @@ public ElementId[] getProblemIdList() { Enumeration enumeration = problemIdHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - ElementId element = (ElementId) enumeration.nextElement(); + ElementId element = enumeration.nextElement(); elementIds[i] = element; i++; } return elementIds; } - + /** * Get list of ElementIds for the languages in the filter list. - * + * * @return list of element ids. */ public ElementId[] getLanguageIdList() { @@ -822,7 +958,7 @@ public ElementId[] getLanguageIdList() { Enumeration enumeration = languageIdHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - ElementId element = (ElementId) enumeration.nextElement(); + ElementId element = enumeration.nextElement(); elementIds[i] = element; i++; } @@ -831,7 +967,7 @@ public ElementId[] getLanguageIdList() { /** * Get list of ElementIds for the sites in the filter list. - * + * * @return list of element ids. */ public Integer[] getSiteIdList() { @@ -839,16 +975,16 @@ public Integer[] getSiteIdList() { Enumeration enumeration = siteIdHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - Integer element = (Integer) enumeration.nextElement(); + Integer element = enumeration.nextElement(); elementIds[i] = element; i++; } return elementIds; } - + /** * Get list of ClientIds for the accounts in the filter list. - * + * * @return list of ClientId. */ public ClientId[] getAccountList() { @@ -856,16 +992,16 @@ public ClientId[] getAccountList() { Enumeration enumeration = clientIdHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - ClientId element = (ClientId) enumeration.nextElement(); + ClientId element = enumeration.nextElement(); clientIds[i] = element; i++; } return clientIds; } - + /** * Get list of Types in filter. - * + * * @return */ public ClientType.Type[] getClientTypes() { @@ -873,14 +1009,30 @@ public ClientType.Type[] getClientTypes() { Enumeration enumeration = clientTypeHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - ClientType.Type value = (ClientType.Type) enumeration.nextElement(); + ClientType.Type value = enumeration.nextElement(); types[i] = value; i++; } return types; - + } - + + + /** + * Get list of ElementIds for the groups in the filter list. + * + * @return list of element ids. + */ + public ElementId [] getGroupIdList() { + ElementId [] elementIds = new ElementId[groupIdHash.size()]; + int i = 0; + for(ElementId groupElementId : groupIdHash) { + elementIds[i] = groupElementId; + i++; + } + return elementIds; + } + public void setUsingRunStatesFilter(boolean turnOn) { filteringRunStates = turnOn; } @@ -888,7 +1040,7 @@ public void setUsingRunStatesFilter(boolean turnOn) { public boolean isFilteringRunStates() { return filteringRunStates; } - + /** * Add an account to filter with. * @param account @@ -939,11 +1091,12 @@ public void addRunState(RunStates runStates) { } /** - * + * * @param runStates * @return * @deprecated use {@link #matches(RunStates)} */ + @Deprecated public boolean matchesRunState(RunStates runStates) { return matches(runStates); } @@ -956,7 +1109,7 @@ public boolean matches(RunStates runStates) { } } - + public boolean matchesElapsedTime(Run run) { return matchesElapsedTimeSubmission(run); } @@ -969,7 +1122,7 @@ public boolean matchesElapsedTimeSubmission(Submission submission) { long elapsedTime = submission.getElapsedMins(); return matchesElapsedTime(elapsedTime); } - + protected boolean matchesElapsedTime(long elapsedTimeMinutes) { if (filteringElapsedTime) { if (startElapsedTimeMinutes != -1) { @@ -1013,7 +1166,7 @@ public ClarificationStates[] getClarificationStatesList() { Enumeration enumeration = clarificationStateHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - clarificationStates[i] = (ClarificationStates) enumeration.nextElement(); + clarificationStates[i] = enumeration.nextElement(); i++; } return clarificationStates; @@ -1024,17 +1177,18 @@ public RunStates[] getRunStates() { Enumeration enumeration = runStateHash.keys(); int i = 0; while (enumeration.hasMoreElements()) { - runStates[i] = (RunStates) enumeration.nextElement(); + runStates[i] = enumeration.nextElement(); i++; } return runStates; } /** - * + * * @param clarificationStates * @return * @deprecated use {@link #matches(ClarificationStates)} */ + @Deprecated public boolean matchesClarificationState(ClarificationStates clarificationStates) { return matches(clarificationStates); } @@ -1056,12 +1210,13 @@ public void clearClarificationStateList() { clarificationStateHash = new Hashtable(); } + @Override public String toString() { if (filterEnabled) { - + String filterInfo = "Filter ON"; - + if (thisSiteOnly || filteringSites) { filterInfo += " Site(s) "; } @@ -1081,10 +1236,13 @@ public String toString() { filterInfo += " clar state(s)"; } if (filteringAccounts) { - filterInfo += " account(s))"; + filterInfo += " account(s)"; } if (filteringPermissions) { - filterInfo += " permissions(s))"; + filterInfo += " permissions(s)"; + } + if (filteringGroups) { + filterInfo += " groups(s)"; } return filterInfo; @@ -1146,22 +1304,23 @@ public void setFilterOff() { public void setFilter (boolean filterOn){ filterEnabled = filterOn; } - + public void setFilterOn () { filterEnabled = true; } /** * Is one of the filters active?. - * + * * @return true if filter is on, false if not filtering. */ public boolean isFilterOn() { if (filterEnabled) { - return filteringSites || filteringAccounts || filteringClarificationStates || filteringProblems + return filteringSites || filteringAccounts || filteringClarificationStates || filteringProblems || filteringJudgements || filteringLanguages || filteringElapsedTime || filteringRunStates || filteringPermissions - || thisSiteOnly; + || thisSiteOnly + || filteringGroups; } else { return false; } @@ -1179,10 +1338,10 @@ public void clearElapsedTimeRange() { endElapsedTimeMinutes = -1; filteringElapsedTime = false; } - + /** * Count the runs that match this filter. - * + * * @param runs - list of runs * @return number of runs that match this filter. */ @@ -1210,7 +1369,7 @@ public int countClarifications(Clarification[] clarifications) { } return count; } - + /** * Count the accounts that match this filter. * @param accounts @@ -1225,7 +1384,7 @@ public int countAccounts (Account [] accounts) { } return count; } - + /** * Count the ClientIds that match this filter. * @param clientIds @@ -1240,10 +1399,10 @@ public int countClientIds (ClientId [] clientIds) { } return count; } - + /** * Returns list of runs for current filter. - * + * * @param runs * @return list of runs which match this filter. */ @@ -1257,12 +1416,12 @@ public Run[] getRuns(Run[] runs) { } } - return (Run[]) listOfRuns.toArray(new Run[listOfRuns.size()]); + return listOfRuns.toArray(new Run[listOfRuns.size()]); } /** * Returns list of clarifications for current filter. - * + * * @param clarifications * @return list of clarifications which match this filter. */ @@ -1276,7 +1435,7 @@ public Clarification[] getClarifications(Clarification[] clarifications) { } } - return (Clarification[]) list.toArray(new Clarification[list.size()]); + return list.toArray(new Clarification[list.size()]); } public boolean matchesElapsedTime(RunTestCase runTestCaseResult) { @@ -1286,7 +1445,7 @@ public boolean matchesElapsedTime(RunTestCase runTestCaseResult) { public void setFilteringDeleted(boolean b) { filteringDeleted = b; - + } } diff --git a/src/edu/csus/ecs/pc2/core/model/IInternalContest.java b/src/edu/csus/ecs/pc2/core/model/IInternalContest.java index 313564515..c0a38db85 100644 --- a/src/edu/csus/ecs/pc2/core/model/IInternalContest.java +++ b/src/edu/csus/ecs/pc2/core/model/IInternalContest.java @@ -1,4 +1,4 @@ -// 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.core.model; import java.io.IOException; @@ -24,7 +24,7 @@ import edu.csus.ecs.pc2.profile.ProfileCloneSettings; /** Specifies methods used to manipulate contest data. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -33,7 +33,7 @@ public interface IInternalContest { /** - * + * * @return contest password */ String getContestPassword(); @@ -44,21 +44,21 @@ public interface IInternalContest { void setContestPassword(String contestPassword); void addLanguage(Language language); - + void deleteLanguage(Language language); - + void addProblem(Problem problem); - + void addReplaySetting (ReplaySetting replaySetting); - + /** * Add Notification into model from remote site. - * + * * Use {@link #acceptNotification(Notification)} to add {@link Notification} * to this site/server/model. - * + * * @see #acceptNotification(Notification) - * + * * @param notification * @throws IOException * @throws ClassNotFoundException @@ -69,11 +69,11 @@ public interface IInternalContest { void addProblem(Problem problem, ProblemDataFiles problemDataFiles); void deleteProblem(Problem problem); - + void deleteReplaySetting(ReplaySetting replaySetting); - + void addCategory(Category category); - + void deleteCategory(Category category); void addContestTime(ContestTime contestTime); @@ -84,7 +84,7 @@ public interface IInternalContest { /** * Replace judgement list. - * + * * @param judgementList */ void setJudgementList(Judgement[] judgementList); @@ -102,38 +102,38 @@ public interface IInternalContest { void connectionDropped(ConnectionHandlerID connectionHandlerID); void updateSite(Site site); - + /** * Update whether Site is running a particular profile or not. - * + * * @param site * @param inProfile - * @param status + * @param status */ void updateSiteStatus(Site site, Profile inProfile, Status status); void updateLanguage(Language language); - + void updateProblem(Problem problem); - + void updateReplaySetting(ReplaySetting replaySetting); - + /** * Update existing Notification. - * + * * Use {@link #acceptNotification(Notification)} to add Notification * into this server/model. - * + * * @see #acceptNotification(Notification) - * + * * @param notification * @throws IOException * @throws ClassNotFoundException * @throws FileSecurityException */ void updateNotification (Notification notification) throws IOException, ClassNotFoundException, FileSecurityException; - + void updateCategory(Category category); void updateProblem(Problem problem, ProblemDataFiles problemDataFiles); @@ -152,7 +152,7 @@ public interface IInternalContest { /** * Update current contest time values. - * + * * @param contestTime */ void updateContestTime(ContestTime contestTime); @@ -167,58 +167,58 @@ public interface IInternalContest { /** * Start InternalContest Clock at site. - * + * * @param siteNumber */ void startContest(int siteNumber); /** * Stop InternalContest Clock at site. - * + * * @param siteNumber */ void stopContest(int siteNumber); /** * Add a run into the contest data, return updated Submitted Run. - * + * * @param submittedRun * @return Submitted Run with id and elapsedtime - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ Run acceptRun(Run submittedRun, RunFiles runFiles) throws IOException, ClassNotFoundException, FileSecurityException; - - + + /** * Add a run into the runList. - * + * * This just adds the run into the list, unlike acceptRun, this does not increment the run. - * + * * @param run - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void addRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException; /** * Add a run from the server, a run ready to be judged. - * + * * @param run * submitted run * @param runFiles * submitted run files (like source files) - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void addRun(Run run, RunFiles runFiles, ClientId whoCheckedOutRunId) throws IOException, ClassNotFoundException, FileSecurityException; /** * Add/archive run and runfiles. - * + * * @param run * @param runFiles * @throws IOException @@ -230,7 +230,7 @@ public interface IInternalContest { /** * Add new accounts. - * + * * @see edu.csus.ecs.pc2.core.list.AccountList#generateNewAccounts(edu.csus.ecs.pc2.core.model.ClientType.Type, int, int, edu.csus.ecs.pc2.core.list.AccountList.PasswordType, int, boolean) * @param clientTypeName * name of client type, "team", "judge", etc. @@ -244,7 +244,7 @@ public interface IInternalContest { /** * Add new accounts, client number starting at startNumber. - * + * * @see edu.csus.ecs.pc2.core.list.AccountList#generateNewAccounts(edu.csus.ecs.pc2.core.model.ClientType.Type, int, int, edu.csus.ecs.pc2.core.list.AccountList.PasswordType, int, boolean) * @param clientTypeName * @param count @@ -256,7 +256,7 @@ public interface IInternalContest { /** * Add new sites. - * + * * @param count * @param active */ @@ -264,46 +264,46 @@ public interface IInternalContest { /** * Add a new account listener. - * + * * @param accountListener */ void addAccountListener(IAccountListener accountListener); /** * Remove a account listener. - * + * * @param accountListener */ void removeAccountListener(IAccountListener accountListener); /** * Remove a balloonSettings listener. - * + * * @param balloonSettingsListener */ void removeBalloonSettingsListener(IBalloonSettingsListener balloonSettingsListener); /** * Fetch all defined groups. - * + * * @return array of Group */ Group[] getGroups(); /** * Fetch all defined problems. - * + * * @return array of Problem */ Problem[] getProblems(); - + ReplaySetting[] getReplaySettings(); - + Notification [] getNotifications(); - + /** * Fetch all defined categories, includes non-hidden Problems too. - * + * * @return array of Category. */ Category[] getCategories(); @@ -320,11 +320,11 @@ public interface IInternalContest { /** * Fetch all defined languages. - * + * * @return array of Language */ Language[] getLanguages(); - + PlaybackInfo [] getPlaybackInfos(); /** @@ -342,17 +342,17 @@ public interface IInternalContest { void removeRunListener(IRunListener runListener); void addClarificationListener(IClarificationListener clarificationListener); - + void addNotificationListener (INotificationListener notificationListener); void removeClarificationListener(IClarificationListener clarificationListener); void addProblemListener(IProblemListener problemListener); - + void removeNotificationListener (INotificationListener notificationListener); void removeProblemListener(IProblemListener problemListener); - + void addCategoryListener(ICategoryListener categoryListener); void removeCategoryListener(ICategoryListener categoryListener); @@ -360,11 +360,11 @@ public interface IInternalContest { void addLanguageListener(ILanguageListener languageListener); void removeLanguageListener(ILanguageListener languageListener); - + void addPlayBackEventListener(IPlayBackEventListener playBackEventListener); - + void removePlayBackEventListener(IPlayBackEventListener playBackEventListener); - + void addChangePasswordListener(IChangePasswordListener changePasswordListener); void removeChangePasswordListener(IChangePasswordListener changePasswordListener); @@ -402,7 +402,7 @@ public interface IInternalContest { void removeProfileListener(IProfileListener profileListener); Run getRun(ElementId id); - + Account[] getAccounts(); Vector getAccounts(Type type, int siteNumber); @@ -415,7 +415,7 @@ public interface IInternalContest { /** * return true if account exists and valid password matches. - * + * * @param clientId * @param password * @return true if valid password for input clientId @@ -424,7 +424,7 @@ public interface IInternalContest { /** * add any login from any site. - * + * * @param clientId * @param connectionHandlerID */ @@ -432,9 +432,9 @@ public interface IInternalContest { /** * Add only as a local login. - * + * * This is for servers who are locally logged in. - * + * * @param clientId * @param connectionHandlerID */ @@ -442,7 +442,7 @@ public interface IInternalContest { /** * Add login as a remote login. - * + * * @param clientId * @param connectionHandlerID */ @@ -450,7 +450,7 @@ public interface IInternalContest { /** * Lookup a client id given a ConnectionHandlerID. - * + * * @param connectionHandlerID * @return ClientId or null if not found. */ @@ -458,7 +458,7 @@ public interface IInternalContest { /** * is local login (login to this server). - * + * * @param sourceId * @return true if logged in. */ @@ -466,9 +466,9 @@ public interface IInternalContest { /** * Return date when client logged in or null if not logged in. - * + * * Should use {@link #isLocalLoggedIn(ClientId)} not this method to check whether client logged in. - * + * * @param clientId * @return date client logged in */ @@ -476,7 +476,7 @@ public interface IInternalContest { /** * Is logged into remote server. - * + * * @param clientId * @return true if client is logged into remote server */ @@ -489,7 +489,7 @@ public interface IInternalContest { /** * Lookup ConnectionHandlerIDs for the specified ClientId. - * + * * @param clientId * @return a (possibly empty, but never null) Enumeration of all CollectionHandlerIDs for the specified client. */ @@ -503,21 +503,21 @@ public interface IInternalContest { /** * Logoff, remove user from login list. - * + * * @param clientId */ void removeLogin(ClientId clientId); /** * Remove client from remote login list - * + * * @param clientId */ void removeRemoteLogin(ClientId clientId); /** * Get all connection ids for all sites. - * + * * @return all sites connection ids. */ ConnectionHandlerID[] getConnectionHandlerIDs(); @@ -534,7 +534,7 @@ public interface IInternalContest { /** * Is current module allowed to perform action based on their permissions?. - * + * * @param type * @return true if allowed, false if not. */ @@ -542,7 +542,7 @@ public interface IInternalContest { /** * Is client/module allowed to perform action based on their permissions?. - * + * * @param clientId * @param type * @return true if allowed, false if not. @@ -551,10 +551,10 @@ public interface IInternalContest { /** * Get all logins in contest matching the specified {@link ClientType}. - * + * * Note that if multiple simultaneous logins are allowed for the specified ClientType and there are currently * multiple logins for a given clientId, the returned array will contain one element for each such login. - * + * * @param type the type of client for which logins are sought. * @return array of all logged in clients of the specified type. */ @@ -562,7 +562,7 @@ public interface IInternalContest { /** * Get all locally logged in clients of a given {@link ClientType.Type}. - * + * * @param type the type of clients to search for. * @return array of all local logged in clients of the specified type. */ @@ -570,7 +570,7 @@ public interface IInternalContest { /** * Get clients logged into other servers. - * + * * @param type * @return array of all remote logged in clients */ @@ -585,7 +585,7 @@ public interface IInternalContest { /** * Load all submissions off disk. - * + * */ void initializeSubmissions(int siteNumber); @@ -596,20 +596,20 @@ public interface IInternalContest { /** * Update run status. - * + * * @param run * @param judgementRecord * @param runResultFiles * @param whoUpdatedRun - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void runUpdated(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles, ClientId whoUpdatedRun) throws IOException, ClassNotFoundException, FileSecurityException; /** * Update Run compiling/executing/vaidating status. - * + * * @param run * @param status * @param whoUpdatedRun @@ -618,30 +618,30 @@ public interface IInternalContest { /** * Add a run not available, notify listeners. - * + * * @param run */ void runNotAvailable(Run run); /** * Attempt to checkout run. - * + * * @param run * @param whoChangedRun * @throws RunUnavailableException * - if run already checked out or not NEW. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ Run checkoutRun(Run run, ClientId whoChangedRun, boolean reCheckoutRun, boolean computerJudge) throws RunUnavailableException, IOException, ClassNotFoundException, FileSecurityException; /** * Unconditionally update the run. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException - * + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException + * */ void updateRun(Run run, ClientId whoChangedRun) throws IOException, ClassNotFoundException, FileSecurityException; @@ -649,17 +649,17 @@ public interface IInternalContest { /** * Get submitted files for input run. - * + * * @param run - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ RunFiles getRunFiles(Run run) throws IOException, ClassNotFoundException, FileSecurityException; /** * Get run result files for input run. - * + * * @param run * @param judgementRecord * @throws FileSecurityException @@ -670,31 +670,31 @@ public interface IInternalContest { /** * Add a run judgement. - * + * * @param run * @param judgementRecord * @param runResultFiles * @param judgeId - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void addRunJudgement(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles, ClientId judgeId) throws IOException, ClassNotFoundException, FileSecurityException; /** * Cancel a checked out run. - * + * * @param run * @param fromId - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void cancelRunCheckOut(Run run, ClientId fromId) throws UnableToUncheckoutRunException, IOException, ClassNotFoundException, FileSecurityException; /** * Returns which user has checked out the input run. - * + * * @param run * @return clientId, or null if not checked out. */ @@ -702,7 +702,7 @@ public interface IInternalContest { /** * Returns which user has checked out the input run. - * + * * @param judgeID * @return Element id of runs. */ @@ -710,11 +710,11 @@ public interface IInternalContest { /** * Available run, a canceled run. - * + * * @param run - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void availableRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException; @@ -727,11 +727,11 @@ public interface IInternalContest { /** * add clarification into model. - * + * * @param clarification - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void addClarification(Clarification clarification) throws IOException, ClassNotFoundException, FileSecurityException; @@ -739,7 +739,7 @@ public interface IInternalContest { /** * add new clarification onto server. - * + * * @param clarification * @return clarification with new id and timestamp. */ @@ -751,62 +751,62 @@ public interface IInternalContest { /** * Add Notification to model, assign number and time. - * + * * Sets {@link Notification#setNumber(int)} and {@link Notification#setElapsedMS(long)}. - * - * @param notification + * + * @param notification * @return */ Notification acceptNotification (Notification notification); - + /** * remove clarification from model. - * + * * @param clarification - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void removeClarification(Clarification clarification) throws IOException, ClassNotFoundException, FileSecurityException; /** * change/update clarification in model. - * + * * @param clarification - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ void changeClarification(Clarification clarification) throws IOException, ClassNotFoundException, FileSecurityException; /** * Attempt to checkout clarification. - * + * * @param clarification * @param whoChangedClar * @throws ClarificationUnavailableException * - if clar already checked out or not NEW. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ Clarification checkoutClarification(Clarification clar, ClientId whoChangedClar) throws ClarificationUnavailableException, IOException, ClassNotFoundException, FileSecurityException; /** * Add a clarification not available, notify listeners. - * + * * @param run */ void clarificationNotAvailable(Clarification clar); Language getLanguage(ElementId elementId); - + PlaybackInfo getPlaybackInfo(ElementId elementId); Problem getProblem(ElementId elementId); - + Notification getNotification(ElementId elementId); - + Category getCategory (ElementId elementId); Judgement getJudgement(ElementId elementId); @@ -841,21 +841,21 @@ public interface IInternalContest { void addContestInformation(ContestInformation contestInformation); void updateContestInformation(ContestInformation contestInformation); - + void addContestInformationListener(IContestInformationListener contestInformationListener); void removeContestInformationListener(IContestInformationListener contestInformationListener); /** * Get contest info, like title. - * + * * @return the contest info */ ContestInformation getContestInformation(); /** * Get individual client settings - * + * * @return client settings */ ClientSettings getClientSettings(); @@ -866,25 +866,25 @@ public interface IInternalContest { /** * Maximum MSecs for each retry. - * + * * Used to calculate a "random" connection retry, when client disconnected. - * + * * @return milliseconds */ int getMaxRetryMSecs(); /** * Maximum number of connection retries before taking action. - * + * * Action might be putting a GUI in front of the user. - * + * * @return maximum unattended retry attempts */ int getMaxConnectionRetries(); /** * Get all Balloon Settings. - * + * * @return array of the Balloon Settings */ BalloonSettings[] getBalloonSettings(); @@ -911,7 +911,7 @@ public interface IInternalContest { /** * Password change attempted. - * + * * @param success * if true, password was changed * @param clientId @@ -923,32 +923,32 @@ public interface IInternalContest { /** * Clear data caches and fires REFRESH_ALL events. - * + * * Clears caches for clars and runs , removes all checked out status'. * Resets next run Id and next Clarification id. - * + * */ void resetSubmissionData(); - + /** * Clear all data except submissions. - * + * * clears accounts, languages, problems, etc. */ void resetConfigurationData(); /** * Send run compiling, executing and validating status. - * + * * @return */ boolean isSendAdditionalRunStatusMessages(); /** * Is this object from the current contest configuration ?. - * + * * Different Profiles have different contest identifiers, this method returns true if the contest info is from the same configuration. - * + * * @param object * @return */ @@ -956,34 +956,34 @@ public interface IInternalContest { /** * return a Contest Identifier. - * + * * The Contest Identifier is stored in the Profile, so the {@link #setProfile(Profile)} method is used to update the Profile. - * - * + * + * * @return a unique identifier for this configuration/profile. */ String getContestIdentifier(); /** * Set the contest identifier - * + * * @param contestId */ void setContestIdentifier(String contestId); /** * Set the Profile/configuration identifier for this contest instance. - * + * */ void setProfile(Profile profile); /** * Get the Profile/configuration identifier for this contest instance. - * + * * @return */ Profile getProfile(); - + /** * Get saved Profile/configuration. * @param id @@ -993,7 +993,7 @@ public interface IInternalContest { /** * Get list of profiles. - * + * * @return */ Profile[] getProfiles(); @@ -1008,21 +1008,21 @@ public interface IInternalContest { /** * Set the storage reader/writer. - * + * * @param storage */ void setStorage(IStorage storage); - + /** * Get the Storage - * + * * @return */ IStorage getStorage(); /** * Read in configuration, and submissions, unchecks out submissions. - * + * * @param siteNumber * @param log * @return @@ -1031,17 +1031,17 @@ public interface IInternalContest { * @throws FileSecurityException */ boolean readConfiguration(int siteNumber, Log log) throws IOException, ClassNotFoundException, FileSecurityException; - + /** * Clone a contest settings/submissions based on settings. - * + * * Does not switch profiles - just makes a copy of it based on the * current profile and the ProfileCloneSettings. *

    * To switch profiles see/use {@link edu.csus.ecs.pc2.core.model.IInternalController#cloneProfile(Profile, ProfileCloneSettings, boolean)}; - * + * * @param contest - * @param newProfile + * @param newProfile * @param settings * @return cloned contest * @throws ProfileCloneException @@ -1050,11 +1050,11 @@ public interface IInternalContest { /** * Get settings used to clone/create this profile. - * + * * @return settings used to create/generate this profile or null */ ProfileCloneSettings getProfileCloneSettings(); - + /** * Store the profile clone settings that created the current Profile. * @param profileCloneSettings @@ -1064,7 +1064,7 @@ public interface IInternalContest { /** * clone/import Clarifications from input contest. - * + * * @param inputContest * @param newProfile * @throws ProfileCloneException @@ -1073,7 +1073,7 @@ public interface IInternalContest { /** * clone/import Run and Run Files from input contest. - * + * * @param inputContest * @param newProfile * @throws ProfileCloneException @@ -1092,14 +1092,14 @@ public interface IInternalContest { /** * Copied all current login and connection established information. - * + * * @param newContest contest to copy information to. */ void cloneAllLoginAndConnections(IInternalContest newContest) throws CloneException; /** * Are RunFiles present? - * + * * @param run * @return true if RunFiles are available. */ @@ -1107,7 +1107,7 @@ public interface IInternalContest { /** * A message has come from source to destination. - * + * * @param source * @param destination * @param message @@ -1122,8 +1122,8 @@ public interface IInternalContest { void updateRunFiles(Run run, RunFiles runFiles) throws IOException, ClassNotFoundException, FileSecurityException; /** - * - * @return null if no data or FinalizeData + * + * @return null if no data or FinalizeData */ FinalizeData getFinalizeData(); @@ -1135,7 +1135,7 @@ public interface IInternalContest { /** * get notification for team and problem. - * + * * @param submitter * @param problemId * @return null if no notification @@ -1147,7 +1147,7 @@ public interface IInternalContest { * if no categories are present. */ void setupDefaultCategories(); - + void addPlaybackInfo(PlaybackInfo playbackInfo); void deletePlaybackInfo(PlaybackInfo playbackInfo); @@ -1157,16 +1157,16 @@ public interface IInternalContest { void resetPlaybackInfo(PlaybackInfo playbackInfo); void stopReplayPlaybackInfo(PlaybackInfo playbackInfo); - + void startReplayPlaybackInfo(PlaybackInfo playbackInfo); - + PlaybackManager getPlaybackManager(); - + void setPlaybackManager(PlaybackManager playbackManager); - + /** * Create and assign team names and password to a new account. - * + * * @param displayName * @param memberNames * @return @@ -1174,11 +1174,11 @@ public interface IInternalContest { Account autoRegisterTeam (String displayName, String [] memberNames, String password); /** - * + * * @return event feed defs. */ EventFeedDefinition[] getEventFeedDefinitions(); - + /** * Add Event Feed Definition. * @param eventFeedDefinition @@ -1187,20 +1187,20 @@ public interface IInternalContest { /** * Delete Event Feed Definition. - * + * * @param eventFeedDefinition */ void deleteEventFeedDefinition(EventFeedDefinition eventFeedDefinition); /** * Update Event Feed Definition. - * + * * @param eventFeedDefinition */ void updateEventFeedDefinition(EventFeedDefinition eventFeedDefinition); /** - * + * * @param elementId * @return single contest event def. */ @@ -1209,7 +1209,7 @@ public interface IInternalContest { void addLanguages(Language[] languages); void updateLanguages(Language[] languages); - + void addGroups(Group[] groups); void updateGroups(Group[] groups); @@ -1223,7 +1223,7 @@ public interface IInternalContest { /** * Set command line parrameters - * @param parseArguments command line arguments + * @param parseArguments command line arguments */ void setCommandLineArguments(ParseArguments parseArguments); @@ -1236,7 +1236,7 @@ public interface IInternalContest { /** * get the parameter after the optionName. - * + * * @param optionNaeme * @return */ diff --git a/src/edu/csus/ecs/pc2/core/model/InternalContest.java b/src/edu/csus/ecs/pc2/core/model/InternalContest.java index cf1213d19..848d50d24 100644 --- a/src/edu/csus/ecs/pc2/core/model/InternalContest.java +++ b/src/edu/csus/ecs/pc2/core/model/InternalContest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2022 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.core.model; import java.io.File; @@ -66,10 +66,10 @@ /** * Implementation of IInternalContest - the contest model. - * + * * This model is not responsible for logic, just storage. So, for example, {@link #cancelRunCheckOut(Run, ClientId)} will simply update the Run but will not check whether the run should be cancelled. * The InternalController should be used to check whether a Run should be cancelled. Other logic of this sort is in the InternalController, not the InternalContest. - * + * * @author pc2@ecs.csus.edu */ @@ -82,15 +82,15 @@ public class InternalContest implements IInternalContest { private Vector clarificationListenerList = new Vector(); private Vector problemListenerList = new Vector(); - + private Vector categoryListenerList = new Vector(); - + private Vector notificationListenerList = new Vector(); private Vector languageListenerList = new Vector(); - + private Vector playBackEventListenerList = new Vector(); - + private Vector profileListenerList = new Vector(); private Vector changePasswordListenerList = new Vector(); @@ -108,15 +108,15 @@ public class InternalContest implements IInternalContest { private Vector clientSettingsListenerList = new Vector(); private Vector contestInformationListenerList = new Vector(); - + private Vector balloonSettingsListenerList = new Vector(); private Vector groupListenerList = new Vector(); private Vector messageListenerList = new Vector(); - - private Vector eventFeedDefinitionListenerList =new Vector(); - + + private Vector eventFeedDefinitionListenerList =new Vector(); + /** * Contains name of client (judge or admin) who checks out the run. */ @@ -132,21 +132,21 @@ public class InternalContest implements IInternalContest { private BalloonSettingsList balloonSettingsList = new BalloonSettingsList(); private Vector accountListenerList = new Vector(); - + private GroupList groupList = new GroupList(); - + private Problem generalProblem = null; /** * Logins on this site. - * + * * These are all logins that have been authenticated by the local server. */ private LoginList localLoginList = new LoginList(); /** * Logins on other sites. - * + * * These are all the logins that have been authenticated by a remote server. */ private LoginList remoteLoginList = new LoginList(); @@ -189,9 +189,9 @@ public class InternalContest implements IInternalContest { * List of all defined problems. Contains deleted problems too. */ private ProblemList problemList = new ProblemList(); - + private CategoryList categoryList = new CategoryList(); - + private NotificationList notificationList = new NotificationList(); private ProblemDataFilesList problemDataFilesList = new ProblemDataFilesList(); @@ -200,16 +200,16 @@ public class InternalContest implements IInternalContest { * List of all problems displayed to users, in order. Does not contain deleted problems. */ private ProblemDisplayList problemDisplayList = new ProblemDisplayList(); - + private CategoryDisplayList categoryDisplayList = new CategoryDisplayList(); /** * List of all languages. Contains deleted problems too. */ private LanguageList languageList = new LanguageList(); - + private PlaybackInfoList playbackInfoList = new PlaybackInfoList(); - + /** * List of all profiles. */ @@ -234,15 +234,15 @@ public class InternalContest implements IInternalContest { * List of all judgements. Contains deleted judgements too. */ private JudgementList judgementList = new JudgementList(); - + private SecurityMessageHandler securityMessageHandler; - + private Profile profile = null; private String contestIdentifier = null; private ConfigurationIO configurationIO = null; - + private IStorage storage = null; private String contestPassword = null; @@ -250,10 +250,10 @@ public class InternalContest implements IInternalContest { private ProfileCloneSettings profileCloneSettings = null; private FinalizeData finalizeData = null; - + // TODO 670 add list for ReplaySetting, add load and save too private ReplaySetting replaySettingTemp = null; - + private PlaybackManager playbackManager = new PlaybackManager(); private EventFeedDefinitionsList eventFeedDefinitionsList = null; @@ -271,6 +271,7 @@ private Site createFakeSite(int nextSiteNumber) { return site; } + @Override public void initializeSubmissions(int siteNum) { try{ initializeSubmissions(siteNum, true); @@ -278,15 +279,15 @@ public void initializeSubmissions(int siteNum) { logException("Trouble loading clarifications from disk ", e); } } - + /** * Load submission and other information from disk. - * + * * @param siteNum * @param uncheckoutSubmissions - changes all checked out submissions to un-checkedout - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ public void initializeSubmissions(int siteNum, boolean uncheckoutSubmissions) throws IOException, ClassNotFoundException, FileSecurityException { @@ -317,7 +318,7 @@ public void initializeSubmissions(int siteNum, boolean uncheckoutSubmissions) th } /** - * For each run reset to a non-checked out state. + * For each run reset to a non-checked out state. * @param siteNum */ private void resetRunStatus(int siteNum) { @@ -339,7 +340,7 @@ private void resetRunStatus(int siteNum) { } // Can not have a judged run with no judgment records - set to NEW as above if(status == RunStates.JUDGED && runs[i].getJudgementRecord() == null) { - status = RunStates.NEW; + status = RunStates.NEW; } if (!runs[i].getStatus().equals(status)) { StaticLog.info("Changing Run " + runs[i].getElementId() + " from " + runs[i].getStatus() + "to NEW"); @@ -358,6 +359,7 @@ private void resetRunStatus(int siteNum) { } } + @Override public void initializeStartupData(int siteNum) { if (siteList.size() == 0) { @@ -384,45 +386,55 @@ public void initializeStartupData(int siteNum) { if (getAccounts(Type.ADMINISTRATOR) == null || getAccounts(Type.ADMINISTRATOR, siteNum).size() == 0) { generateNewAccounts(ClientType.Type.ADMINISTRATOR.toString(), 1, true); } - + } + @Override public void addRunListener(IRunListener runListener) { runListenerList.addElement(runListener); } + @Override public void removeRunListener(IRunListener runListener) { runListenerList.removeElement(runListener); } + @Override public void addClarificationListener(IClarificationListener clarificationListener) { clarificationListenerList.addElement(clarificationListener); } + @Override public void removeClarificationListener(IClarificationListener clarificationListener) { clarificationListenerList.remove(clarificationListener); } + @Override public void addContestTimeListener(IContestTimeListener contestTimeListener) { contestTimeListenerList.addElement(contestTimeListener); } + @Override public void removeContestTimeListener(IContestTimeListener contestTimeListener) { contestTimeListenerList.removeElement(contestTimeListener); } + @Override public void addJudgementListener(IJudgementListener judgementListener) { judgementListenerList.addElement(judgementListener); } + @Override public void removeJudgementListener(IJudgementListener judgementListener) { judgementListenerList.remove(judgementListener); } - + + @Override public void addMessageListener(IMessageListener messageListener) { messageListenerList.addElement(messageListener); } + @Override public void removeMessageListener(IMessageListener messageListener) { messageListenerList.removeElement(messageListener); } @@ -479,7 +491,7 @@ private void fireProblemListener(ProblemEvent problemEvent) { } } } - + private void fireEventFeedDefinitionListener(EventFeedDefinitionEvent eventFeedDefinitionEvent) { for (int i = 0; i < problemListenerList.size(); i++) { @@ -494,9 +506,9 @@ private void fireEventFeedDefinitionListener(EventFeedDefinitionEvent eventFeedD } } } - + private void fireCategoryListener(Category category, CategoryEvent.Action action) { - + CategoryEvent categoryEvent = new CategoryEvent(action, category); for (int i = 0; i < categoryListenerList.size(); i++) { @@ -511,9 +523,9 @@ private void fireCategoryListener(Category category, CategoryEvent.Action action } } } - + private void fireNotificationListener(Notification notification, NotificationEvent.Action added) { - + NotificationEvent notificationEvent = new NotificationEvent(NotificationEvent.Action.CHANGED, notification); for (int i = 0; i < notificationListenerList.size(); i++) { @@ -547,11 +559,11 @@ private void fireLanguageListener(LanguageEvent languageEvent) { } } } - + private void firePlaybackInfosListener(PlayBackEvent.Action action, PlaybackInfo playbackInfo) { - + PlayBackEvent playBackEvent = new PlayBackEvent(action, playbackInfo); - + for (int i = 0; i < playBackEventListenerList.size(); i++) { if (playBackEvent.getAction() == PlayBackEvent.Action.ADDED) { playBackEventListenerList.elementAt(i).playbackAdded(playBackEvent); @@ -580,7 +592,7 @@ private void fireLoginListener(LoginEvent loginEvent) { loginListenerList.elementAt(i).loginDenied(loginEvent); } else if (loginEvent.getAction() == LoginEvent.Action.REFRESH_ALL) { loginListenerList.elementAt(i).loginRefreshAll(loginEvent); - + } else { throw new UnsupportedOperationException("Unknown login action " + loginEvent.getAction()); } @@ -667,6 +679,7 @@ private void fireMessageListener(MessageEvent messageEvent) { } } + @Override public void addLocalLogin(ClientId inClientId, ConnectionHandlerID connectionHandlerID) { localLoginList.add(inClientId, connectionHandlerID); LoginEvent loginEvent = new LoginEvent(LoginEvent.Action.NEW_LOGIN, inClientId, connectionHandlerID, "New"); @@ -674,12 +687,14 @@ public void addLocalLogin(ClientId inClientId, ConnectionHandlerID connectionHan } + @Override public void addRemoteLogin(ClientId inClientId, ConnectionHandlerID connectionHandlerID) { remoteLoginList.add(inClientId, connectionHandlerID); LoginEvent loginEvent = new LoginEvent(LoginEvent.Action.NEW_LOGIN, inClientId, connectionHandlerID, "New"); fireLoginListener(loginEvent); } + @Override public void addLogin(ClientId inClientId, ConnectionHandlerID connectionHandlerID) { if (inClientId.getSiteNumber() == siteNumber) { localLoginList.add(inClientId, connectionHandlerID); @@ -690,11 +705,13 @@ public void addLogin(ClientId inClientId, ConnectionHandlerID connectionHandlerI fireLoginListener(loginEvent); } + @Override public void loginDenied(ClientId clientId, ConnectionHandlerID connectionHandlerID, String message) { LoginEvent loginEvent = new LoginEvent(LoginEvent.Action.LOGIN_DENIED, clientId, connectionHandlerID, message); fireLoginListener(loginEvent); } - + + @Override public void addLanguage(Language language) { languageDisplayList.add(language); languageList.add(language); @@ -702,43 +719,49 @@ public void addLanguage(Language language) { fireLanguageListener(languageEvent); } + @Override public void addProblem(Problem problem) { // this will be null on the 1st server ClientId me = getClientId(); if (me != null) { Account account = getAccount(me); - if (account != null && account.isTeam() && account.getGroupId() != null) { - // If is a team and has been assigned a group. - - Group group = getGroup(account.getGroupId()); - if (!problem.canView(group)) { - /** - * This is a team, and this team's group is not allowed to view this problem - */ - // Do not add this problem to the list of problems. + if (account != null && account.isTeam() && account.getGroupIds() != null) { + // If is a team and has been assigned one or more groups. + boolean canSeeProblem = false; + for(ElementId elementId : account.getGroupIds()) { + Group group = getGroup(elementId); + if (problem.canView(group)) { + canSeeProblem = true; + break; + } + } + if(!canSeeProblem) { return; } } } - + problemDisplayList.add(problem); problemList.add(problem); ProblemEvent problemEvent = new ProblemEvent(ProblemEvent.Action.ADDED, problem); fireProblemListener(problemEvent); } - + + @Override public void addCategory(Category category) { categoryDisplayList.add(category); categoryList.add(category); fireCategoryListener(category, CategoryEvent.Action.ADDED); } - + + @Override public void addNotification(Notification notification) throws IOException, ClassNotFoundException, FileSecurityException { notificationList.add(notification); fireNotificationListener(notification, NotificationEvent.Action.ADDED); } + @Override public void addJudgement(Judgement judgement) { if (judgement.getSiteNumber() == 0) { judgement.setSiteNumber(getSiteNumber()); @@ -749,29 +772,34 @@ public void addJudgement(Judgement judgement) { fireJudgementListener(judgementEvent); } + @Override public void addSite(Site site) { siteList.add(site); SiteEvent siteEvent = new SiteEvent(SiteEvent.Action.ADDED, site); fireSiteListener(siteEvent); } + @Override public void updateSite(Site site) { siteList.update(site); SiteEvent siteEvent = new SiteEvent(SiteEvent.Action.CHANGED, site); fireSiteListener(siteEvent); } - + + @Override public void updateSiteStatus(Site site, Profile inProfile, Status status) { SiteEvent siteEvent = new SiteEvent(SiteEvent.Action.STATUS_CHANGE, site, inProfile, status); fireSiteListener(siteEvent); } + @Override public void addAccount(Account account) { accountList.add(account); AccountEvent accountEvent = new AccountEvent(AccountEvent.Action.ADDED, account); fireAccountListener(accountEvent); } + @Override public void addAccounts(Account[] accounts) { for (Account account : accounts) { accountList.add(account); @@ -780,16 +808,18 @@ public void addAccounts(Account[] accounts) { fireAccountListener(accountEvent); } + @Override public Judgement[] getJudgements() { return judgementDisplayList.getList(); } /** * Accept Run, add new run into server. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ + @Override public Run acceptRun(Run run, RunFiles runFiles) throws IOException, ClassNotFoundException, FileSecurityException { run.setElapsedMS(getContestTime().getElapsedMS()); run.setSiteNumber(getSiteNumber()); @@ -807,10 +837,11 @@ public Run acceptRun(Run run, RunFiles runFiles) throws IOException, ClassNotFou /** * Accept Clarification, add clar into this server. - * + * * @param clarification * @return the accepted Clarification */ + @Override public Clarification acceptClarification(Clarification clarification) { clarification.setElapsedMS(getContestTime().getElapsedMS()); clarification.setSiteNumber(getSiteNumber()); @@ -826,10 +857,11 @@ public Clarification acceptClarification(Clarification clarification) { } catch (FileSecurityException e) { logException(e); } - + return null; } + @Override public void answerClarification(Clarification clarification, String answer, ClientId whoAnsweredIt, boolean sendToAll) { if (clarificationList.get(clarification) != null) { @@ -847,7 +879,7 @@ public void answerClarification(Clarification clarification, String answer, Clie } catch (FileSecurityException e) { logException(e); } - + } else { try { clarificationList.add(clarification); @@ -865,6 +897,7 @@ public void answerClarification(Clarification clarification, String answer, Clie } } + @Override public void updateClarification(Clarification clarification, ClientId whoChangedIt) { try { clarificationList.updateClarification(clarification); @@ -883,6 +916,7 @@ public void updateClarification(Clarification clarification, ClientId whoChanged } } + @Override public void clarificationNotAvailable(Clarification clar) { ClarificationEvent clarEvent = new ClarificationEvent(ClarificationEvent.Action.CLARIFICATION_NOT_AVAILABLE, clar); fireClarificationListener(clarEvent); @@ -890,18 +924,20 @@ public void clarificationNotAvailable(Clarification clar) { /** * Add a run to run list, notify listeners. - * + * * @param run - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ + @Override public void addRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException { runList.add(run); RunEvent runEvent = new RunEvent(RunEvent.Action.ADDED, run, null, null); fireRunListener(runEvent); } + @Override public void addRun(Run run, RunFiles runFiles, ClientId whoCheckedOutRunId) throws IOException, ClassNotFoundException, FileSecurityException { runList.add(run); runFilesList.add(run, runFiles); @@ -910,13 +946,15 @@ public void addRun(Run run, RunFiles runFiles, ClientId whoCheckedOutRunId) thro fireRunListener(runEvent); } + @Override public void addRun(Run run, RunFiles runFiles) throws IOException, ClassNotFoundException, FileSecurityException { runList.add(run); runFilesList.add(run, runFiles); RunEvent runEvent = new RunEvent(RunEvent.Action.ADDED, run, runFiles, null); fireRunListener(runEvent); } - + + @Override public void availableRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException { runList.add(run); RunEvent runEvent = new RunEvent(RunEvent.Action.RUN_AVAILABLE, run, null, null); @@ -925,12 +963,13 @@ public void availableRun(Run run) throws IOException, ClassNotFoundException, Fi /** * Generate accounts. - * + * * @see edu.csus.ecs.pc2.core.model.IInternalContest#generateNewAccounts(java.lang.String, int, int, boolean) * @param clientTypeName * @param count * @param active */ + @Override public Vector generateNewAccounts(String clientTypeName, int count, boolean active) { return generateNewAccounts(clientTypeName, count, 1, active); } @@ -938,6 +977,7 @@ public Vector generateNewAccounts(String clientTypeName, int count, boo /** * @see edu.csus.ecs.pc2.core.model.IInternalContest#generateNewAccounts(java.lang.String, int, int, boolean) */ + @Override public Vector generateNewAccounts(String clientTypeName, int count, int startNumber, boolean active) { ClientType.Type type = ClientType.Type.valueOf(clientTypeName.toUpperCase()); @@ -953,10 +993,11 @@ public Vector generateNewAccounts(String clientTypeName, int count, int /** * Generate new sites - * + * * @param count * @param active */ + @Override public void generateNewSites(int count, boolean active) { int numSites = siteList.size(); @@ -970,11 +1011,13 @@ public void generateNewSites(int count, boolean active) { } } + @Override public void addAccountListener(IAccountListener accountListener) { accountListenerList.addElement(accountListener); } + @Override public void removeAccountListener(IAccountListener accountListener) { accountListenerList.removeElement(accountListener); } @@ -996,22 +1039,27 @@ private void fireAccountListener(AccountEvent accountEvent) { } } + @Override public Problem[] getProblems() { return problemDisplayList.getList(); } - + + @Override public Category[] getCategories() { return categoryDisplayList.getList(); } + @Override public Language[] getLanguages() { return languageDisplayList.getList(); } + @Override public ClientId getClientId() { return localClientId; } + @Override public void setClientId(ClientId clientId) { this.localClientId = clientId; // if (isServer()){ @@ -1024,13 +1072,13 @@ public void setClientId(ClientId clientId) { // runFilesList = new RunFilesList(clientId.getSiteNumber()); // clarificationList = new ClarificationList(clientId.getSiteNumber(), true); // } - + try { securityMessageHandler = new SecurityMessageHandler(clientId); } catch (Exception e) { logException("Trouble establishing SecurityMessageHandler", e); } - + if (isAllowed(localClientId, Permission.Type.ALLOWED_TO_FETCH_RUN)){ // This client can cache run files if ( ! runFilesList.isWriteToDisk() ){ @@ -1042,15 +1090,18 @@ public void setClientId(ClientId clientId) { } } } - + + @Override public Log getSecurityAlertLog() { return securityMessageHandler.getLog(); } + @Override public Site[] getSites() { return siteList.getList(); } + @Override public String getTitle() { ClientId id = getClientId(); if (id == null) { @@ -1061,98 +1112,122 @@ public String getTitle() { return titleCase + " " + id.getClientNumber() + " (Site " + id.getSiteNumber() + ")"; } + @Override public void addProblemListener(IProblemListener problemListener) { problemListenerList.addElement(problemListener); } + @Override public void removeProblemListener(IProblemListener problemListener) { problemListenerList.remove(problemListener); } + @Override public void addCategoryListener(ICategoryListener categoryListener) { categoryListenerList.addElement(categoryListener); } + @Override public void removeCategoryListener(ICategoryListener categoryListener) { categoryListenerList.remove(categoryListener); } + @Override public void addLanguageListener(ILanguageListener languageListener) { languageListenerList.addElement(languageListener); } + @Override public void removeLanguageListener(ILanguageListener languageListener) { languageListenerList.remove(languageListener); } - + + @Override public void addPlayBackEventListener(IPlayBackEventListener playBackEventListener) { playBackEventListenerList.addElement(playBackEventListener); } + @Override public void removePlayBackEventListener(IPlayBackEventListener playBackEventListener) { playBackEventListenerList.remove(playBackEventListener); } - + + @Override public void addChangePasswordListener(IChangePasswordListener changePasswordListener){ changePasswordListenerList.add(changePasswordListener); } + @Override public void removeChangePasswordListener(IChangePasswordListener changePasswordListener){ changePasswordListenerList.remove(changePasswordListener); } + @Override public void addLoginListener(ILoginListener loginListener) { loginListenerList.addElement(loginListener); } + @Override public void removeLoginListener(ILoginListener loginListener) { loginListenerList.remove(loginListener); } + @Override public void addSiteListener(ISiteListener siteListener) { siteListenerList.add(siteListener); } + @Override public void removeSiteListener(ISiteListener siteListener) { siteListenerList.remove(siteListener); } + @Override public void addConnectionListener(IConnectionListener connectionListener) { connectionListenerList.addElement(connectionListener); } + @Override public void removeConnectionListener(IConnectionListener connectionListener) { connectionListenerList.remove(connectionListener); } - + + @Override public void addBalloonSettingsListener(IBalloonSettingsListener balloonSettingsListener) { balloonSettingsListenerList.addElement(balloonSettingsListener); } - + + @Override public void removeBalloonSettingsListener(IBalloonSettingsListener balloonSettingsListener) { balloonSettingsListenerList.remove(balloonSettingsListener); } + @Override public Run getRun(ElementId id) { return runList.get(id); } + @Override public Clarification getClarification(ElementId id) { return clarificationList.get(id); } + @Override public Vector getAccounts(Type type, int inSiteNumber) { return accountList.getAccounts(type, inSiteNumber); } + @Override public Vector getAccounts(Type type) { return accountList.getAccounts(type); } + @Override public boolean isValidLoginAndPassword(ClientId inClientId, String password) { return accountList.isValidLoginAndPassword(inClientId, password); } + @Override public ClientId getLoginClientId(ConnectionHandlerID connectionHandlerID) { ClientId clientId = localLoginList.getClientId(connectionHandlerID); if (clientId == null) { @@ -1161,47 +1236,56 @@ public ClientId getLoginClientId(ConnectionHandlerID connectionHandlerID) { return clientId; } + @Override public boolean isLoggedIn() { return localClientId != null; } + @Override public boolean isRemoteLoggedIn(ClientId clientId) { return remoteLoginList.isLoggedIn(clientId); } + @Override public boolean isLocalLoggedIn(ClientId clientId) { return localLoginList.isLoggedIn(clientId); } - + + @Override public Date getLocalLoggedInDate (ClientId clientId){ return localLoginList.getLoggedInDate(clientId); } + @Override public Enumeration getConnectionHandlerIDs(ClientId sourceId) { Enumeration connectionHandlerIDs = localLoginList.getConnectionHandlerIDs(sourceId); - + //TODO: what is the logic behind returning REMOTE connections if and only if there aren't any LOCAL connections? // It seems like either this method should return ALL connections (Local AND Remote), or should always only return Local... - + if (connectionHandlerIDs == null || !connectionHandlerIDs.hasMoreElements()) { connectionHandlerIDs = remoteLoginList.getConnectionHandlerIDs(sourceId); } return connectionHandlerIDs; } + @Override public ClientId getClientId(ConnectionHandlerID connectionHandlerID){ return localLoginList.getClientId(connectionHandlerID); } - + + @Override public boolean isConnected(ConnectionHandlerID connectionHandlerID) { return localConnectionHandlerList.get(connectionHandlerID) != null; } + @Override public boolean isConnectedToRemoteSite(ConnectionHandlerID connectionHandlerID) { // TODO enh - the remoteConnectionHandlerList is never populated?!? used ?!? return remoteConnectionHandlerList.get(connectionHandlerID) != null; } + @Override public ConnectionHandlerID[] getConnectionHandleIDs() { ConnectionHandlerID[] localList = localConnectionHandlerList.getList(); @@ -1223,10 +1307,11 @@ public ConnectionHandlerID[] getConnectionHandleIDs() { /** * @throws IllegalArgumentException if the specified ClientId has a null ConnectionHandlerID. */ + @Override public void removeRemoteLogin(ClientId clientIdToRemove) { ConnectionHandlerID connectionHandlerID = clientIdToRemove.getConnectionHandlerID(); - - //this method should never be called with a ClientId containing a null ConnectionHandlerID; however, the addition of + + //this method should never be called with a ClientId containing a null ConnectionHandlerID; however, the addition of // "multiple login" support may have left some place where this is inadvertently true. //The following is an effort to catch/identify such situations. if (connectionHandlerID==null) { @@ -1242,7 +1327,7 @@ public void removeRemoteLogin(ClientId clientIdToRemove) { } else { removeWasSuccessful = false; } - + //check the return value from remove() and log the results if (removeWasSuccessful) { logMessage(Level.INFO, "removed " + clientIdToRemove + " from remote login list"); @@ -1257,10 +1342,11 @@ public void removeRemoteLogin(ClientId clientIdToRemove) { /** * @throws IllegalArgumentException if the specified ClientId has a null ConnectionHandlerID. */ + @Override public void removeLogin(ClientId clientIdToRemove) { ConnectionHandlerID connectionHandlerID = clientIdToRemove.getConnectionHandlerID(); - //this method should never be called with a ClientId containing a null ConnectionHandlerID; however, the addition of + //this method should never be called with a ClientId containing a null ConnectionHandlerID; however, the addition of // "multiple login" support may have left some place where this is inadvertently true. //The following is an effort to catch/identify such situations. if (connectionHandlerID==null) { @@ -1269,7 +1355,7 @@ public void removeLogin(ClientId clientIdToRemove) { logException("InternalContest.removeLogin() called with null ConnectionHandlerID in ClientId " + clientIdToRemove, e); throw e; } - + boolean clientToRemoveIsLocal = isLocalLoggedIn(clientIdToRemove) ; String removalTargetList = clientToRemoveIsLocal ? "local" : "remote"; boolean removeWasSuccessful ; @@ -1279,7 +1365,7 @@ public void removeLogin(ClientId clientIdToRemove) { } else { removeWasSuccessful = remoteLoginList.remove(clientIdToRemove); } - + //check the return value from remove() and log the results if (removeWasSuccessful) { logMessage(Level.INFO, "removed " + clientIdToRemove + " from " + removalTargetList + " login list"); @@ -1291,19 +1377,23 @@ public void removeLogin(ClientId clientIdToRemove) { fireLoginListener(loginEvent); } + @Override public int getSiteNumber() { return siteNumber; } + @Override public void setSiteNumber(int number) { this.siteNumber = number; } - - + + + @Override public boolean isAllowed(Permission.Type type) { return isAllowed(getClientId(), type); } + @Override public boolean isAllowed(ClientId clientId, Permission.Type type) { Account account = getAccount(clientId); if (account == null) { @@ -1320,18 +1410,22 @@ public boolean isAllowed(ClientId clientId, Permission.Type type) { /** * Get this site's contest time. */ + @Override public ContestTime getContestTime() { return getContestTime(getSiteNumber()); } + @Override public ContestTime getContestTime(int inSiteNumber) { return contestTimeList.get(inSiteNumber); } + @Override public ContestTime[] getContestTimes() { return contestTimeList.getList(); } + @Override public void startContest(int inSiteNumber) { ContestTime contestTime = getContestTime(inSiteNumber); if (contestTime != null) { @@ -1343,17 +1437,19 @@ public void startContest(int inSiteNumber) { } } + @Override public void stopContest(int inSiteNumber) { ContestTime contestTime = getContestTime(inSiteNumber); if (contestTime != null) { contestTime.stopContestClock(); ContestTimeEvent contestTimeEvent = new ContestTimeEvent(ContestTimeEvent.Action.CLOCK_STOPPED, contestTime, inSiteNumber); - fireContestTimeListener(contestTimeEvent); + fireContestTimeListener(contestTimeEvent); } else { throw new SecurityException("Unable to stop clock site " + inSiteNumber + " not found"); } } + @Override public void addContestTime(ContestTime contestTime) { if (contestTime == null) { throw new IllegalArgumentException("contestTime is null"); @@ -1367,18 +1463,20 @@ public void addContestTime(ContestTime contestTime) { } } + @Override public ClientId[] getLocalLoggedInClients(Type type) { Enumeration localClients = localLoginList.getClients(type); Vector v = new Vector(); while (localClients.hasMoreElements()) { - ClientId element = (ClientId) localClients.nextElement(); + ClientId element = localClients.nextElement(); v.addElement(element); } - return (ClientId[]) v.toArray(new ClientId[v.size()]); + return v.toArray(new ClientId[v.size()]); } + @Override public ClientId[] getRemoteLoggedInClients(Type type) { Enumeration remoteClients = remoteLoginList.getClients(type); @@ -1386,23 +1484,24 @@ public ClientId[] getRemoteLoggedInClients(Type type) { Vector v = new Vector(); while (remoteClients.hasMoreElements()) { - ClientId element = (ClientId) remoteClients.nextElement(); + ClientId element = remoteClients.nextElement(); v.addElement(element); } - return (ClientId[]) v.toArray(new ClientId[v.size()]); + return v.toArray(new ClientId[v.size()]); } /** * Returns an array containing all logins in this contest matching the specified {@link ClientType}. - * + * * Note that if multiple simultaneous logins are allowed for the specified ClientType and there are currently * multiple logins for a given clientId, the returned array will contain one element for each such login. - * + * * @param type the type of client for which logins are sought. * @return array of all logged in clients of the specified type. */ + @Override public ClientId[] getAllLoggedInClients(Type type) { Enumeration localClients = localLoginList.getClients(type); Enumeration remoteClients = remoteLoginList.getClients(type); @@ -1410,22 +1509,24 @@ public ClientId[] getAllLoggedInClients(Type type) { Vector v = new Vector(); while (localClients.hasMoreElements()) { - ClientId element = (ClientId) localClients.nextElement(); + ClientId element = localClients.nextElement(); v.addElement(element); } while (remoteClients.hasMoreElements()) { - ClientId element = (ClientId) remoteClients.nextElement(); + ClientId element = remoteClients.nextElement(); v.addElement(element); } - return (ClientId[]) v.toArray(new ClientId[v.size()]); + return v.toArray(new ClientId[v.size()]); } + @Override public Run[] getRuns() { return runList.getList(); } + @Override public void runUpdated(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles, ClientId whoUpdatedRun) throws IOException, ClassNotFoundException, FileSecurityException { // TODO handle run RunResultsFiles @@ -1437,7 +1538,8 @@ public void runUpdated(Run run, JudgementRecord judgementRecord, RunResultFiles runEvent.setWhoModifiedRun(whoUpdatedRun); fireRunListener(runEvent); } - + + @Override public void updateRunStatus(Run run, RunExecutionStatus status, ClientId whoUpdatedRun) { RunEvent.Action action = null; @@ -1459,13 +1561,15 @@ public void updateRunStatus(Run run, RunExecutionStatus status, ClientId whoUpda fireRunListener(runEvent); } + @Override public void runNotAvailable(Run run) { RunEvent runEvent = new RunEvent(RunEvent.Action.RUN_NOT_AVAILABLE, run, null, null); fireRunListener(runEvent); } - + + @Override public Run checkoutRun(Run run, ClientId whoChangedRun, boolean reCheckoutRun, boolean computerJudge) throws RunUnavailableException, IOException, ClassNotFoundException, FileSecurityException { - + synchronized (runCheckOutList) { ClientId clientId = runCheckOutList.get(run.getElementId()); @@ -1473,24 +1577,24 @@ public Run checkoutRun(Run run, ClientId whoChangedRun, boolean reCheckoutRun, b // Run checked out throw new RunUnavailableException("Client " + clientId + " already checked out run " + run.getNumber() + " (site " + run.getSiteNumber() + ")"); } - + Run newRun = runList.get(run.getElementId()); - + if (newRun == null){ throw new RunUnavailableException("Run "+ run.getNumber() + " (site " + run.getSiteNumber() + ") not found"); } - + boolean canBeCheckedOut = newRun.getStatus().equals(RunStates.NEW) || newRun.getStatus().equals(RunStates.QUEUED_FOR_COMPUTER_JUDGEMENT) || newRun.getStatus().equals(RunStates.MANUAL_REVIEW); - + if (reCheckoutRun && run.isJudged()){ canBeCheckedOut = true; } - + if (canBeCheckedOut){ runCheckOutList.put(newRun.getElementId(), whoChangedRun); newRun.setStatus(RunStates.BEING_JUDGED); - + if (reCheckoutRun){ newRun.setStatus(RunStates.BEING_RE_JUDGED); } @@ -1499,27 +1603,29 @@ public Run checkoutRun(Run run, ClientId whoChangedRun, boolean reCheckoutRun, b } else { throw new RunUnavailableException("Client " + whoChangedRun + " can not check out run " + run.getNumber() + " (site " + run.getSiteNumber() + ")"); } - + } } - + + @Override public void updateRun(Run run, ClientId whoChangedRun) throws IOException, ClassNotFoundException, FileSecurityException { updateRun (run, null, whoChangedRun, null); } + @Override public void updateRun(Run run, RunFiles runFiles, ClientId whoChangedRun, RunResultFiles[] runResultFiles) throws IOException, ClassNotFoundException, FileSecurityException { /** - * Should this run be un-checked out (removed from the checked out list) ? + * Should this run be un-checked out (removed from the checked out list) ? */ boolean unCheckoutRun = false; - + /** * Should this run be added to the checkout list ? */ boolean checkOutRun = false; - + switch (run.getStatus()) { case CHECKED_OUT: case BEING_JUDGED: @@ -1547,99 +1653,102 @@ public void updateRun(Run run, RunFiles runFiles, ClientId whoChangedRun, RunRes } } } - + runList.updateRun(run); - + if (runFilesList.isCacheRunFiles()){ runFilesList.add(run, runFiles); } - + if (! runResultFilesList.isWriteToDisk() && runResultFiles != null && runResultFiles.length > 0){ runResultFilesList.add(run, run.getJudgementRecord(), runResultFiles[runResultFiles.length-1]); } - + RunEvent runEvent = new RunEvent(RunEvent.Action.CHANGED, runList.get(run), runFiles, runResultFiles); runEvent.setWhoModifiedRun(whoChangedRun); if (run.getStatus().equals(RunStates.BEING_JUDGED) || run.getStatus().equals(RunStates.BEING_RE_JUDGED) ){ runEvent.setDetailedAction(RunEvent.Action.CHECKEDOUT_RUN); } - + if (checkOutRun) { runEvent.setSentToClientId(whoChangedRun); } fireRunListener(runEvent); } - + + @Override public RunFiles getRunFiles(Run run) throws IOException, ClassNotFoundException, FileSecurityException { return runFilesList.getRunFiles(run); } - + + @Override public boolean isRunFilesPresent(Run run) throws IOException, ClassNotFoundException, FileSecurityException { return runFilesList.isRunFilesPresent(run); } + @Override public void addRunJudgement(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles, ClientId whoJudgedItId) throws IOException, ClassNotFoundException, FileSecurityException { Run theRun = runList.get(run); - + if (theRun != null ) { - + if (judgementRecord != null) { - + //at this point we know we have a non-null Run and a non-null JudgementRecord; // get the Ids of who's trying to add the judgement to the run and who (if anyone) has checked out the run ClientId whoIsChangingItId = judgementRecord.getJudgerClientId(); ClientId whoCheckedItOutId = runCheckOutList.get(run.getElementId()); - + //assume it's NOT ok to add the judgement, until we verify who is requesting the add boolean okToAddJudgement = false ; - + //see if it's a superuser trying to add the judgement if (whoIsChangingItId.getClientType().equals(Type.ADMINISTRATOR) || whoIsChangingItId.getClientType().equals(Type.FEEDER) ) { - //TODO: the above test should probably be consolidated into a single method something like "isSuperUser()". + //TODO: the above test should probably be consolidated into a single method something like "isSuperUser()". // The PROBLEM with this is that there are different PERMISSIONS involved. For example, a Feeder invokes this method when it // has the "EDIT_RUN" permission -- but there exists another Permission named "ADD_JUDGEMENTS"; such a method would have to // figure out what permission(s) need to be required to be a "superuser". Work deferred for now. jlc 8/2/21 - + okToAddJudgement = true; - + } else { - + //it's not a superuser trying to add the judgement; see if anyone has checked out the run if (whoCheckedItOutId == null) { - + // No one has checked out this run and it's not a "super-user" (Admin or Feeder) that's trying to add a judgement to it -- that's not logical... Exception ex = new Exception("addRunJudgement - run not in checkedout list ('whoCheckedOut' is null) and requestor is not Admin or Feeder "); logException("Odd that. ", ex); - + okToAddJudgement = false; //note that this variable should already be false here anyway.... this is for insurance - + } else { - + //someone has checked out the run; see if it is the client who's trying to change it that has it checked out if (whoIsChangingItId.equals(whoCheckedItOutId)) { - + //the changer is the client who checked it out; we'll allow that okToAddJudgement = true; - + } else { - + // The client who submitted this add-judgement request is different than the client who checked out the run (and we already know the requestor is not a superuser); // that's not a situation we're willing to allow Exception ex = new Exception("addRunJudgement - the client who checked this run out is not the same as the client who's changing it, " + "and the client who's changing it is not a 'superuser' (Admin or Feeder)" ); logException(ex); - + okToAddJudgement = false; //note that this variable should already be false here anyway.... this is for insurance - + } - } - } - + } + } + if (okToAddJudgement) { - + boolean manualReview = getProblem(theRun.getProblemId()).isManualReview(); - + // pass in the original run which has the testCases runList.updateRun(run, judgementRecord, manualReview); // this sets run to JUDGED and saves the testCases runResultFilesList.add(theRun, judgementRecord, runResultFiles); @@ -1651,24 +1760,25 @@ public void addRunJudgement(Run run, JudgementRecord judgementRecord, RunResultF RunEvent runEvent = new RunEvent(RunEvent.Action.CHANGED, theRun, null, null); fireRunListener(runEvent); } - + } else { - + //We've been asked to add a null judgementRecord to a run, which we can't do... //TODO: need to log this as an error, but there's no local log, no getLog(), etc... // use StaticLog?? } - + } else { - + //We've been asked to add a judgement to a null run, which we can't do... //TODO: need to log this as an error, but there's no local log, no getLog(), etc... // use StaticLog?? } } + @Override public void cancelRunCheckOut(Run run, ClientId fromId) throws UnableToUncheckoutRunException, IOException, ClassNotFoundException, FileSecurityException { - + // TODO Security check, code needed to insure that only // certain accounts can cancel a checkout @@ -1703,11 +1813,11 @@ public void cancelRunCheckOut(Run run, ClientId fromId) throws UnableToUncheckou } else if (theRun.getStatus().equals(RunStates.BEING_RE_JUDGED)) { theRun.setStatus(RunStates.JUDGED); } - + synchronized (runCheckOutList) { runCheckOutList.remove(theRun.getElementId()); } - + runList.updateRun(theRun); theRun = runList.get(theRun); @@ -1719,35 +1829,39 @@ protected boolean isAdministrator(ClientId clientId) { return clientId.getClientType().equals(ClientType.Type.ADMINISTRATOR); } + @Override public ClientId getRunCheckedOutBy(Run run) { return runCheckOutList.get(run.getElementId()); } + @Override public ElementId[] getRunIdsCheckedOutBy(ClientId judgeID) { ElementId runId; ClientId cID; - + Vector v = new Vector(); Enumeration runIDs = runCheckOutList.keys(); - + while (runIDs.hasMoreElements()) { - - runId = (ElementId) runIDs.nextElement(); - + + runId = runIDs.nextElement(); + cID = runCheckOutList.get(runId); - + if (cID.equals(judgeID)) { v.addElement(runId); } } - - return (ElementId[]) v.toArray(new ElementId[v.size()]); + + return v.toArray(new ElementId[v.size()]); } - + + @Override public Clarification[] getClarifications() { return clarificationList.getList(); } - + + @Override public void passwordChanged(boolean success, ClientId clientId, String message) { Action action = Action.PASSWORD_NOT_CHANGED; if (success) { @@ -1771,6 +1885,7 @@ private void firePasswordChangeListener(PasswordChangeEvent passwordChangeEvent) } } + @Override public void updateLanguage(Language language) { languageList.update(language); languageDisplayList.update(language); @@ -1778,20 +1893,23 @@ public void updateLanguage(Language language) { fireLanguageListener(languageEvent); } + @Override public void updateProblem(Problem problem) { problemList.update(problem); problemDisplayList.update(problem); ProblemEvent problemEvent = new ProblemEvent(ProblemEvent.Action.CHANGED, problem); fireProblemListener(problemEvent); } - + + @Override public void updateCategory(Category category) { categoryList.update(category); categoryDisplayList.update(category); fireCategoryListener(category, CategoryEvent.Action.CHANGED); } + @Override public void updateContestTime(ContestTime contestTime, int inSiteNumber) { if (contestTime == null) { throw new IllegalArgumentException("contestTime is null"); @@ -1805,6 +1923,7 @@ public void updateContestTime(ContestTime contestTime, int inSiteNumber) { } + @Override public void updateContestTime(ContestTime contestTime) { if (contestTime == null) { throw new IllegalArgumentException("contestTime is null"); @@ -1814,6 +1933,7 @@ public void updateContestTime(ContestTime contestTime) { fireContestTimeListener(contestTimeEvent); } + @Override public void updateJudgement(Judgement judgement) { judgementDisplayList.update(judgement); judgementList.update(judgement); @@ -1821,18 +1941,21 @@ public void updateJudgement(Judgement judgement) { fireJudgementListener(judgementEvent); } + @Override public void changeSite(Site site) { siteList.update(site); SiteEvent siteEvent = new SiteEvent(SiteEvent.Action.CHANGED, site); fireSiteListener(siteEvent); } + @Override public void updateAccount(Account account) { accountList.update(account); AccountEvent accountEvent = new AccountEvent(AccountEvent.Action.CHANGED, account); fireAccountListener(accountEvent); } - + + @Override public void updateAccounts(Account[] accounts) { for (Account account : accounts) { accountList.update(account); @@ -1841,16 +1964,19 @@ public void updateAccounts(Account[] accounts) { fireAccountListener(accountEvent); } + @Override public Language getLanguage(ElementId elementId) { return (Language) languageList.get(elementId); } + @Override public ContestTime getContestTime(ElementId elementId) { - return (ContestTime) contestTimeList.get(elementId); + return contestTimeList.get(elementId); } + @Override public Problem getProblem(ElementId elementId) { - + // TODO eliminate generalProblem if (generalProblem != null && generalProblem.getElementId().equals(elementId)){ return generalProblem; @@ -1862,21 +1988,25 @@ public Problem getProblem(ElementId elementId) { return problem; } } - + + @Override public Category getCategory(ElementId elementId) { return (Category) categoryList.get(elementId); } + @Override public Judgement getJudgement(ElementId elementId) { return (Judgement) judgementList.get(elementId); } + @Override public Account getAccount(ClientId inClientId) { - return (Account) accountList.getAccount(inClientId); + return accountList.getAccount(inClientId); } + @Override public Site getSite(int number) { Site[] sites = siteList.getList(); for (Site site : sites){ @@ -1902,12 +2032,14 @@ private void fireClarificationListener(ClarificationEvent clarificationEvent) { } } + @Override public void addClarification(Clarification clarification) throws IOException, ClassNotFoundException, FileSecurityException { clarificationList.add(clarification); ClarificationEvent clarificationEvent = new ClarificationEvent(ClarificationEvent.Action.ADDED, clarification); fireClarificationListener(clarificationEvent); } + @Override public void addClarification(Clarification clarification, ClientId whoCheckedOutId) throws IOException, ClassNotFoundException, FileSecurityException { clarificationList.add(clarification); ClarificationEvent clarificationEvent = new ClarificationEvent(ClarificationEvent.Action.CHECKEDOUT_CLARIFICATION, clarification); @@ -1915,28 +2047,33 @@ public void addClarification(Clarification clarification, ClientId whoCheckedOut fireClarificationListener(clarificationEvent); } + @Override public void removeClarification(Clarification clarification) throws IOException, ClassNotFoundException, FileSecurityException { clarificationList.delete(clarification); ClarificationEvent clarificationEvent = new ClarificationEvent(ClarificationEvent.Action.DELETED, clarification); fireClarificationListener(clarificationEvent); } + @Override public void changeClarification(Clarification clarification) throws IOException, ClassNotFoundException, FileSecurityException { clarificationList.updateClarification(clarification); ClarificationEvent clarificationEvent = new ClarificationEvent(ClarificationEvent.Action.CHANGED, clarification); fireClarificationListener(clarificationEvent); } + @Override public void connectionEstablished(ConnectionHandlerID connectionHandlerID) { connectionEstablished(connectionHandlerID, new Date()); } + @Override public void connectionEstablished(ConnectionHandlerID connectionHandlerID, Date connectDate) { localConnectionHandlerList.add(connectionHandlerID, connectDate); ConnectionEvent connectionEvent = new ConnectionEvent(ConnectionEvent.Action.ESTABLISHED, connectionHandlerID); fireConnectionListener(connectionEvent); } + @Override public void connectionDropped(ConnectionHandlerID connectionHandlerID) { if (connectionHandlerID != null){ localConnectionHandlerList.remove(connectionHandlerID); @@ -1945,37 +2082,41 @@ public void connectionDropped(ConnectionHandlerID connectionHandlerID) { fireConnectionListener(connectionEvent); } + @Override public ConnectionHandlerID[] getConnectionHandlerIDs() { return localConnectionHandlerList.getList(); } + @Override public Clarification[] getClarifications(ClientId clientId) { Vector clientClarifications = new Vector(); Enumeration enumeration = clarificationList.getClarList(); while (enumeration.hasMoreElements()) { - Clarification clarification = (Clarification) enumeration.nextElement(); + Clarification clarification = enumeration.nextElement(); if (clarification.isSendToAll() || clientId.equals(clarification.getSubmitter())) { clientClarifications.add(clarification); } } - return (Clarification[]) clientClarifications.toArray(new Clarification[clientClarifications.size()]); + return clientClarifications.toArray(new Clarification[clientClarifications.size()]); } + @Override public Run[] getRuns(ClientId clientId) { Vector clientRuns = new Vector(); Enumeration enumeration = runList.getRunList(); while (enumeration.hasMoreElements()) { - Run run = (Run) enumeration.nextElement(); + Run run = enumeration.nextElement(); if (clientId.equals(run.getSubmitter())) { clientRuns.add(run); } } - return (Run[]) clientRuns.toArray(new Run[clientRuns.size()]); + return clientRuns.toArray(new Run[clientRuns.size()]); } + @Override public void addProblem(Problem problem, ProblemDataFiles problemDataFiles) { problemDisplayList.add(problem); problemList.add(problem); @@ -1987,6 +2128,7 @@ public void addProblem(Problem problem, ProblemDataFiles problemDataFiles) { fireProblemListener(problemEvent); } + @Override public void updateProblem(Problem problem, ProblemDataFiles problemDataFiles) { problemList.update(problem); problemDataFilesList.update(problemDataFiles); @@ -1995,14 +2137,17 @@ public void updateProblem(Problem problem, ProblemDataFiles problemDataFiles) { fireProblemListener(problemEvent); } + @Override public ProblemDataFiles getProblemDataFile(Problem problem) { return (ProblemDataFiles) problemDataFilesList.get(problem); } + @Override public ProblemDataFiles[] getProblemDataFiles() { return problemDataFilesList.getList(); } + @Override public void cancelClarificationCheckOut(Clarification clarification, ClientId whoCancelledIt) throws IOException, ClassNotFoundException, FileSecurityException { // TODO verify the canceler has permissions to cancel this clar clarificationList.updateClarification(clarification, ClarificationStates.NEW, whoCancelledIt); @@ -2029,34 +2174,41 @@ private void fireClientSettingsListener(ClientSettingsEvent clientSettingsEvent) } } + @Override public void addClientSettings(ClientSettings clientSettings) { clientSettingsList.add(clientSettings); ClientSettingsEvent clientSettingsEvent = new ClientSettingsEvent(ClientSettingsEvent.Action.ADDED, clientSettings.getClientId(), clientSettings); fireClientSettingsListener(clientSettingsEvent); } + @Override public ClientSettings getClientSettings() { - return (ClientSettings) clientSettingsList.get(getClientId()); + return clientSettingsList.get(getClientId()); } + @Override public ClientSettings getClientSettings(ClientId clientId) { - return (ClientSettings) clientSettingsList.get(clientId); + return clientSettingsList.get(clientId); } + @Override public ClientSettings[] getClientSettingsList() { return clientSettingsList.getList(); } + @Override public void updateClientSettings(ClientSettings clientSettings) { clientSettingsList.update(clientSettings); ClientSettingsEvent clientSettingsEvent = new ClientSettingsEvent(ClientSettingsEvent.Action.CHANGED, clientSettings.getClientId(), clientSettings); fireClientSettingsListener(clientSettingsEvent); } + @Override public void addClientSettingsListener(IClientSettingsListener clientSettingsListener) { clientSettingsListenerList.addElement(clientSettingsListener); } + @Override public void removeClientSettingsListener(IClientSettingsListener clientSettingsListener) { clientSettingsListenerList.remove(clientSettingsListener); } @@ -2078,30 +2230,36 @@ private void fireContestInformationListener(ContestInformationEvent contestInfor } } + @Override public void addContestInformation(ContestInformation inContestInformation) { this.contestInformation = inContestInformation; ContestInformationEvent contestInformationEvent = new ContestInformationEvent(ContestInformationEvent.Action.ADDED, contestInformation); fireContestInformationListener(contestInformationEvent); } + @Override public void updateContestInformation(ContestInformation inContestInformation) { this.contestInformation = inContestInformation; ContestInformationEvent contestInformationEvent = new ContestInformationEvent(ContestInformationEvent.Action.CHANGED, contestInformation); fireContestInformationListener(contestInformationEvent); } - + + @Override public void addContestInformationListener(IContestInformationListener contestInformationListener) { contestInformationListenerList.addElement(contestInformationListener); } + @Override public void removeContestInformationListener(IContestInformationListener contestInformationListener) { contestInformationListenerList.remove(contestInformationListener); } + @Override public ContestInformation getContestInformation() { return contestInformation; } + @Override public void setJudgementList(Judgement[] judgements) { // Remove all judgements from display list and fire listeners @@ -2124,6 +2282,7 @@ public void setJudgementList(Judgement[] judgements) { } } + @Override public void removeJudgement(Judgement judgement) { int idx = judgementDisplayList.indexOf(judgement); @@ -2134,26 +2293,31 @@ public void removeJudgement(Judgement judgement) { } } + @Override public int getMaxRetryMSecs() { return 700; } + @Override public int getMaxConnectionRetries() { return 5; } + @Override public void addBalloonSettings(BalloonSettings balloonSettings) { balloonSettingsList.add(balloonSettings); BalloonSettingsEvent balloonSettingsEvent = new BalloonSettingsEvent(BalloonSettingsEvent.Action.ADDED, balloonSettings); fireBalloonSettingsListener(balloonSettingsEvent); } + @Override public void updateBalloonSettings(BalloonSettings balloonSettings) { balloonSettingsList.update(balloonSettings); BalloonSettingsEvent balloonSettingsEvent = new BalloonSettingsEvent(BalloonSettingsEvent.Action.CHANGED, balloonSettings); fireBalloonSettingsListener(balloonSettingsEvent); } + @Override public BalloonSettings getBalloonSettings(int siteNum) { for (BalloonSettings balloonSettings : balloonSettingsList.getList()) { if (siteNum == balloonSettings.getSiteNumber()) { @@ -2163,14 +2327,17 @@ public BalloonSettings getBalloonSettings(int siteNum) { return null; } + @Override public BalloonSettings[] getBalloonSettings() { return balloonSettingsList.getList(); } + @Override public BalloonSettings getBalloonSettings(ElementId elementId) { return balloonSettingsList.get(elementId); } + @Override public void addGroup(Group group) { groupDisplayList.add(group); groupList.add(group); @@ -2197,6 +2364,7 @@ private void fireGroupListener(GroupEvent groupEvent) { } } + @Override public void removeGroup(Group group) { int idx = groupDisplayList.indexOf(group); if (idx != -1) { @@ -2206,6 +2374,7 @@ public void removeGroup(Group group) { } } + @Override public void updateGroup(Group group) { groupList.update(group); groupDisplayList.update(group); @@ -2213,26 +2382,32 @@ public void updateGroup(Group group) { fireGroupListener(groupEvent); } + @Override public Group[] getGroups() { return groupDisplayList.getList(); } + @Override public void addGroupListener(IGroupListener groupListener) { groupListenerList.addElement(groupListener); } + @Override public void removeGroupListener(IGroupListener groupListener) { groupListenerList.remove(groupListener); } + @Override public Group getGroup(ElementId elementId) { return (Group) groupList.get(elementId); } + @Override public Problem getGeneralProblem() { return generalProblem; } + @Override public void setGeneralProblem(Problem generalProblem) { this.generalProblem = generalProblem; } @@ -2240,6 +2415,7 @@ public void setGeneralProblem(Problem generalProblem) { /* (non-Javadoc) * @see edu.csus.ecs.pc2.core.model.IInternalContest#checkoutClarification(edu.csus.ecs.pc2.core.model.Clarification, edu.csus.ecs.pc2.core.model.ClientId) */ + @Override public Clarification checkoutClarification(Clarification clar, ClientId whoChangedClar) throws ClarificationUnavailableException, IOException, ClassNotFoundException, FileSecurityException { synchronized (clarCheckOutList) { ClientId clientId = clarCheckOutList.get(clar.getElementId()); @@ -2248,13 +2424,13 @@ public Clarification checkoutClarification(Clarification clar, ClientId whoChang // Run checked out throw new ClarificationUnavailableException("Client " + clientId + " already checked out clar " + clar.getNumber() + " (site " + clar.getSiteNumber() + ")"); } - + Clarification newClar = clarificationList.get(clar.getElementId()); - + if (newClar == null){ throw new ClarificationUnavailableException("Run "+ clar.getNumber() + " (site " + clar.getSiteNumber() + ") not found"); } - + if (newClar.getState().equals(ClarificationStates.NEW)){ clarCheckOutList.put(newClar.getElementId(), whoChangedClar); newClar.setState(ClarificationStates.BEING_ANSWERED); @@ -2264,26 +2440,28 @@ public Clarification checkoutClarification(Clarification clar, ClientId whoChang } else { throw new ClarificationUnavailableException("Client " + whoChangedClar + " can not check out clar " + clar.getNumber() + " (site " + clar.getSiteNumber() + ")"); } - + } } /** * Issue a security message, fire listeners. - * + * */ + @Override public void newSecurityMessage(ClientId clientId, String message, String eventName, ContestSecurityException contestSecurityException) { securityMessageHandler.newMessage(clientId, eventName, message, contestSecurityException); } - + /** * Add security message Listener. - * + * * This listener will be given an {@link edu.csus.ecs.pc2.core.security.SecurityMessageEvent} - * when a security message is added/logged. - * + * when a security message is added/logged. + * * @param securityMessageListener */ + @Override public void addSecurityMessageListener(ISecurityMessageListener securityMessageListener) { securityMessageHandler.addSecurityMessageListener(securityMessageListener); } @@ -2292,19 +2470,22 @@ public void addSecurityMessageListener(ISecurityMessageListener securityMessageL * Remove security message listener. * @param securityMessageListener */ + @Override public void removeSecurityMessageListener(ISecurityMessageListener securityMessageListener) { securityMessageHandler.addSecurityMessageListener(securityMessageListener); } + @Override public RunResultFiles[] getRunResultFiles(Run run) throws IOException, ClassNotFoundException, FileSecurityException { - + return runResultFilesList.getRunResultFiles(run); } + @Override public void resetSubmissionData() { - + synchronized (runCheckOutList){ - // FIXME on all client Run listeners (elsewhere) need to uncheckout too + // FIXME on all client Run listeners (elsewhere) need to uncheckout too runCheckOutList = new Hashtable(); } @@ -2320,19 +2501,19 @@ public void resetSubmissionData() { } catch (FileSecurityException e) { logException(e); } - + /** - * Clear submitted files list + * Clear submitted files list */ runFilesList.clearCache(); - + runResultFilesList.clear(); synchronized (clarCheckOutList) { - // FIXME on all client Clar listeners (elsewhere) need to uncheckout too + // FIXME on all client Clar listeners (elsewhere) need to uncheckout too clarCheckOutList = new Hashtable(); } - + /** * Clear all clarifications */ @@ -2346,7 +2527,8 @@ public void resetSubmissionData() { logException(e); } } - + + @Override public void resetConfigurationData() { // GROUPS @@ -2388,13 +2570,13 @@ public void resetConfigurationData() { // Clarification Categories categoryList = new CategoryList(); categoryDisplayList = new CategoryDisplayList(); - + // SITES siteList = new SiteList(); // PROFILES profileList = new ProfilesList(); - + // OTHER SETTINGS profileCloneSettings = null; @@ -2408,10 +2590,12 @@ public void resetConfigurationData() { } + @Override public boolean isSendAdditionalRunStatusMessages() { return contestInformation.isSendAdditionalRunStatusInformation(); } + @Override public void deleteProblem(Problem problem) { problemDisplayList.removeElement(problem); problemList.delete(problem); @@ -2419,13 +2603,15 @@ public void deleteProblem(Problem problem) { ProblemEvent problemEvent = new ProblemEvent(ProblemEvent.Action.DELETED, problem); fireProblemListener(problemEvent); } - + + @Override public void deleteCategory(Category category) { categoryDisplayList.removeElement(category); categoryList.delete(category); fireCategoryListener(category, CategoryEvent.Action.DELETED); } + @Override public void deleteLanguage(Language language) { languageDisplayList.removeElement(language); languageList.delete(language); @@ -2433,6 +2619,7 @@ public void deleteLanguage(Language language) { fireLanguageListener(languageEvent); } + @Override public boolean contestIdMatches(String identifier) { if (profile == null){ return false; @@ -2440,23 +2627,27 @@ public boolean contestIdMatches(String identifier) { return profile.matchesIdentifier(identifier); } + @Override public String getContestIdentifier() { if (profile != null){ return profile.getContestId(); } else { return contestIdentifier; } - + } + @Override public Profile getProfile() { return profile; } - + + @Override public Profile getProfile(ElementId id) { return (Profile) profileList.get(id); } + @Override public void setProfile(Profile profile) { this.profile = profile; if (profileList.size() == 0){ @@ -2464,15 +2655,17 @@ public void setProfile(Profile profile) { } else { updateProfile(profile); } - + setContestIdentifier(profile.getContestId()); - + } + @Override public void setContestIdentifier(String contestId) { contestIdentifier = contestId; } + @Override public Profile addProfile(Profile theProfile) { profileList.add(theProfile); ProfileEvent profileEvent = new ProfileEvent(ProfileEvent.Action.ADDED, theProfile); @@ -2495,43 +2688,49 @@ private void fireProfileListener(ProfileEvent profileEvent) { } } + @Override public void addProfileListener(IProfileListener profileListener) { profileListenerList.add(profileListener); } + @Override public void deleteProfile(Profile theProfile) { profileList.delete(theProfile); ProfileEvent profileEvent = new ProfileEvent(ProfileEvent.Action.DELETED, theProfile); fireProfileListener(profileEvent); } + @Override public Profile[] getProfiles() { return profileList.getList(); } + @Override public void removeProfileListener(IProfileListener profileListener) { profileListenerList.remove(profileListener); } + @Override public Profile updateProfile(Profile theProfile) { profileList.update(theProfile); ProfileEvent profileEvent = new ProfileEvent(ProfileEvent.Action.CHANGED, theProfile); fireProfileListener(profileEvent); return theProfile; } - + + @Override public boolean storeConfiguration(Log log) throws IOException, ClassNotFoundException, FileSecurityException { return configurationIO.store(this, log); } public boolean readConfiguration(int siteNum, Log log, boolean uncheckoutSubmissions) throws IOException, ClassNotFoundException, FileSecurityException { - + String settingsFilename = configurationIO.getFileName(); - + if (! new File(settingsFilename).exists()){ throw new FileNotFoundException("Profile/config file missing "+settingsFilename); } - + boolean loadedConfiguration = configurationIO.loadFromDisk(siteNum, this, log); initializeSubmissions(siteNum, uncheckoutSubmissions); // Initialize contest time if necessary @@ -2542,16 +2741,19 @@ public boolean readConfiguration(int siteNum, Log log, boolean uncheckoutSubmiss } return loadedConfiguration; } - + + @Override public boolean readConfiguration(int siteNum, Log log) throws IOException, ClassNotFoundException, FileSecurityException { return readConfiguration(siteNum, log, true); } + @Override public void setStorage(IStorage storage) { this.storage = storage; configurationIO = new ConfigurationIO(storage); } + @Override public void setupDefaultCategories() { if (getCategories().length == 0){ String []catNames = {"General"}; @@ -2561,18 +2763,22 @@ public void setupDefaultCategories() { } } + @Override public String getContestPassword() { return contestPassword; } + @Override public void setContestPassword(String contestPassword) { this.contestPassword = contestPassword; } + @Override public IStorage getStorage() { return storage; } + @Override public IInternalContest clone(IInternalContest contest, Profile newProfile, ProfileCloneSettings settings) throws ProfileCloneException { // TODO check for existing profile @@ -2588,7 +2794,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof if (!new File(profilePath).isDirectory()) { throw new ProfileCloneException("Unable to use profile dir " + profilePath); } - + String databaseDirectoryName = profilePath + File.separator + "db." + contest.getSiteNumber(); try { @@ -2596,7 +2802,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof } catch (Exception e) { throw new ProfileCloneException("Unable to create DB dir " + profilePath, e); } - + String logDirectoryName = profilePath + File.separator + Log.LOG_DIRECTORY_NAME; try { @@ -2618,14 +2824,14 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof // Save settings that created this new contest/profile so we can // send them to other servers/clients as needed. contest.setProfileCloneSettings(settings); - + if (contest.getAccounts(Type.SERVER) == null) { contest.addAccount(getAccount(getClientId())); } contest.setClientId(getClientId()); contest.setSiteNumber(getClientId().getSiteNumber()); - + if (contest.getAccounts(Type.ADMINISTRATOR) != null) { ClientId adminId = new ClientId(getClientId().getSiteNumber(), Type.ADMINISTRATOR, 1); Account account = getAccount(adminId); @@ -2635,7 +2841,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof contest.generateNewAccounts(ClientType.Type.ADMINISTRATOR.toString(), 1, true); } // without this AJ throws an null pointer exception - ClientSettings clientSettings = getClientSettings(adminId); + ClientSettings clientSettings = getClientSettings(adminId); if (clientSettings != null) { contest.addClientSettings(clientSettings); } @@ -2649,7 +2855,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof newProfile.setDescription(settings.getDescription()); contest.setProfile(newProfile); // This also sets the contest identifier - + for (Profile profile2 : getProfiles()) { if (!profile2.equals(newProfile)) { contest.addProfile(profile2); @@ -2657,7 +2863,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof } contest.setContestPassword(new String(settings.getContestPassword())); - + if (settings.isResetContestTimes()){ for (ContestTime contestTime : getContestTimes()) { ContestTime contestTime2 = clone(contestTime); @@ -2686,7 +2892,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof if (! settings.isCopyGroups()) { for (Account account : contest.getAccounts()) { - account.setGroupId(null); + account.clearGroups(); } } } else { @@ -2705,7 +2911,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof if (settings.isCopyJudgements()) { contest.setJudgementList(getJudgements()); } - + if (contest.getJudgements().length == 0){ Judgement judgement = new Judgement("Yes"); contest.addJudgement(judgement); @@ -2775,14 +2981,14 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof newContestInformation.setContestTitle(settings.getContestTitle()); contest.updateContestInformation(newContestInformation); - + Log tempLog = new Log("profileClone"); try { // Store new configuration -- SAVE IT. contest.storeConfiguration(tempLog); - + } catch (IOException e) { tempLog.log(Log.SEVERE, "Exception storing new config for " + newProfile.getName(), e); throw new ProfileCloneException("Exception storing new config for " + newProfile.getName(), e); @@ -2797,7 +3003,7 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof /** * Updated profiles in .properties/text file. - * + * */ try { ProfileManager profileManager = new ProfileManager(); @@ -2819,14 +3025,15 @@ public IInternalContest clone(IInternalContest contest, Profile newProfile, Prof private ContestTime clone(ContestTime contestTime) { ContestTime time = new ContestTime(contestTime.getSiteNumber()); - + time.setContestLengthSecs(contestTime.getContestLengthSecs()); time.setElapsedSecs(time.getElapsedSecs()); time.setHaltContestAtTimeZero(contestTime.isHaltContestAtTimeZero()); - - return time; + + return time; } + @Override public Account[] getAccounts() { return accountList.getList(); } @@ -2836,37 +3043,41 @@ private void cloneContestSettings(IInternalContest contest, ProfileCloneSettings for (ClientSettings clientSettings : contest.getClientSettingsList()) { if (settings.isCopyProblems()){ contest.addClientSettings(clientSettings); - // FIXME set auto judge filter + // FIXME set auto judge filter // FIXME set balloon list } else { // clear client problem settings clientSettings.setBalloonList(new Hashtable()); clientSettings.setAutoJudgeFilter(new Filter()); } } - + for (BalloonSettings balloonSettings : getBalloonSettings()){ contest.addBalloonSettings(balloonSettings); } - + // FIXME ClientSettings: properties // FIXME ClientSettings: notificationSetting // FIXME ClientSettings: balloonList } + @Override public ProfileCloneSettings getProfileCloneSettings() { return profileCloneSettings; } + @Override public ProfileCloneSettings setProfileCloneSettings(ProfileCloneSettings inProfileCloneSettings) { this.profileCloneSettings = inProfileCloneSettings; return profileCloneSettings; } + @Override public void cloneClarifications(IInternalContest targetContest, Profile newProfile) throws ProfileCloneException { clarificationList.clone(targetContest.getStorage()); } + @Override public void cloneRunsAndRunFiles(IInternalContest targetContest, Profile newProfile) throws ProfileCloneException { runList.clone(targetContest.getStorage()); @@ -2875,7 +3086,7 @@ public void cloneRunsAndRunFiles(IInternalContest targetContest, Profile newProf runResultFilesList.clone(targetContest.getStorage()); } - + private void logException(String message, Exception e) { if (StaticLog.getLog() != null) { @@ -2885,7 +3096,7 @@ private void logException(String message, Exception e) { e.printStackTrace(System.err); } } - + private void logException(Exception e) { if (StaticLog.getLog() != null) { @@ -2894,16 +3105,17 @@ private void logException(Exception e) { e.printStackTrace(System.err); } } - + private void logMessage(java.util.logging.Level level, String message) { if (StaticLog.getLog() != null) { StaticLog.getLog().log(level, message); } else { System.err.println(message); } - + } + @Override public void removeAllListeners() { accountListenerList.removeAllElements(); balloonSettingsListenerList.removeAllElements(); @@ -2925,13 +3137,14 @@ public void removeAllListeners() { messageListenerList.removeAllElements(); } + @Override public void fireAllRefreshEvents() { // FIXME double check that all Events are being refreshed - + ProfileEvent profileEvent = new ProfileEvent(ProfileEvent.Action.REFRESH_ALL, null); fireProfileListener(profileEvent); - + RunEvent runEvent = new RunEvent(RunEvent.Action.REFRESH_ALL, null, null, null); fireRunListener(runEvent); @@ -2989,13 +3202,14 @@ public void fireAllRefreshEvents() { /** * Copies all the current login and connectionHandler data, for both local and remote logins and connectionHandlers, * from the this contest to the specified newContest. - * + * * @param newContest the contest into which the login and connection information will be copied. */ + @Override public void cloneAllLoginAndConnections(IInternalContest newContest) throws CloneException { - + CloneException ex = null; - + /** * Local Logins */ @@ -3004,10 +3218,10 @@ public void cloneAllLoginAndConnections(IInternalContest newContest) throws Clon try { //get a list of (one or possibly more) ConnectionHandlerIDs associated with the current locally logged-in client in the current contest List connectionList = Collections.list(localLoginList.getConnectionHandlerIDs(clientId)); - + //add each of the ConnectionHandlerIDs for the current local client to the list of local connections for the specified client in the new contest for (ConnectionHandlerID connHID : connectionList) { - newContest.addLocalLogin(clientId, connHID); + newContest.addLocalLogin(clientId, connHID); } } catch (Exception e) { ex = new CloneException(e.getMessage(), e.getCause()); @@ -3020,10 +3234,10 @@ public void cloneAllLoginAndConnections(IInternalContest newContest) throws Clon for (ClientId clientId : remoteLoginList.getClientIdList()) { try { - + //get a list of (one or possibly more) ConnectionHandlerIDs associated with the current remotely-logged in client in the current contest List connectionList = Collections.list(remoteLoginList.getConnectionHandlerIDs(clientId)); - + //add each of the ConnectionHandlerIDs for the current remote client to the list of remote connections for the specified client in the new contest for (ConnectionHandlerID connHID : connectionList) { newContest.addRemoteLogin(clientId, connHID); @@ -3056,61 +3270,72 @@ public void cloneAllLoginAndConnections(IInternalContest newContest) throws Clon ex = new CloneException(e.getMessage(), e.getCause()); } } - + if (ex != null){ throw ex; } - + } + @Override public void addMessage(MessageEvent.Area area, ClientId source, ClientId destination, String message) { MessageEvent messageEvent = new MessageEvent(MessageEvent.Action.ADDED, area, message, source, destination); fireMessageListener(messageEvent); } + @Override public void updateRunFiles(Run run, RunFiles runFiles) throws IOException, ClassNotFoundException, FileSecurityException { runFilesList.add(run, runFiles); } + @Override public FinalizeData getFinalizeData() { return finalizeData; } + @Override public void setFinalizeData(FinalizeData data) { finalizeData = data; ContestInformationEvent event = new ContestInformationEvent(contestInformation, data); fireContestInformationListener(event); } + @Override public Notification[] getNotifications() { return notificationList.getList(); } + @Override public void addNotificationListener(INotificationListener notificationListener) { notificationListenerList.add(notificationListener); } + @Override public void removeNotificationListener(INotificationListener notificationListener) { notificationListenerList.remove(notificationListener); } + @Override public Notification getNotification(ElementId elementId) { return notificationList.get(elementId); } + @Override public void updateNotification(Notification notification) throws IOException, ClassNotFoundException, FileSecurityException { notificationList.updateNotification(notification); } + @Override public Notification getNotification(ClientId submitter, ElementId problemId) { return notificationList.get(submitter, problemId); } + @Override public Notification acceptNotification(Notification notification) { notification.setElapsedMS(getContestTime().getElapsedMS()); notification.setSiteNumber(getSiteNumber()); - + try { Notification newNotif = notificationList.addNewNotification(notification); addNotification(newNotif); @@ -3127,33 +3352,37 @@ public Notification acceptNotification(Notification notification) { } + @Override public void addReplaySetting(ReplaySetting replaySetting) { replaySettingTemp = replaySetting; fireReplaySettingListener(ReplaySettingEvent.Action.ADDED, replaySetting); } + @Override public void deleteReplaySetting(ReplaySetting replaySetting) { fireReplaySettingListener(ReplaySettingEvent.Action.DELETED, replaySetting); replaySettingTemp = null; } + @Override public void updateReplaySetting(ReplaySetting replaySetting) { replaySettingTemp = replaySetting; fireReplaySettingListener(ReplaySettingEvent.Action.CHANGED, replaySetting); } - - private void fireReplaySettingListener(ReplaySettingEvent.Action action, ReplaySetting replaySetting) { - + + private void fireReplaySettingListener(ReplaySettingEvent.Action action, ReplaySetting replaySetting) { + // TODO 670 add listener for ReplaySetting - + // ReplaySettingEvent event = new ReplaySettingEvent(action, replaySetting); - + } + @Override public ReplaySetting[] getReplaySettings() { - + // TODO 670 add list for ReplaySetting - + if (replaySettingTemp == null) { // TODO 6670 remove this debugging code replaySettingTemp = new ReplaySetting("Sample Replay Title"); @@ -3163,7 +3392,7 @@ public ReplaySetting[] getReplaySettings() { // replaySettingTemp.setStartSequenceNumber(24); // replaySettingTemp.setLoadFileName("c:\\tmp\\site1.replay.txt"); } - + if (replaySettingTemp == null) { return new ReplaySetting[0]; } else { @@ -3173,62 +3402,75 @@ public ReplaySetting[] getReplaySettings() { } } + @Override public void addPlaybackInfo(PlaybackInfo playbackInfo) { playbackInfoList.add(playbackInfo); firePlaybackInfosListener(PlayBackEvent.Action.ADDED, playbackInfo); } + @Override public void deletePlaybackInfo(PlaybackInfo playbackInfo) { playbackInfoList.delete(playbackInfo); firePlaybackInfosListener(PlayBackEvent.Action.DELETE, playbackInfo); } + @Override public void updatePlaybackInfo(PlaybackInfo playbackInfo) { playbackInfoList.update(playbackInfo); firePlaybackInfosListener(PlayBackEvent.Action.REFRESH_ALL, playbackInfo); } - + + @Override public void resetPlaybackInfo(PlaybackInfo playbackInfo) { playbackInfo.rewind(); firePlaybackInfosListener(PlayBackEvent.Action.RESET_REPLAY, playbackInfo); } + @Override public void stopReplayPlaybackInfo(PlaybackInfo playbackInfo) { playbackInfoList.delete(playbackInfo); firePlaybackInfosListener(PlayBackEvent.Action.STOP_REPLAY, playbackInfo); } - + + @Override public void startReplayPlaybackInfo(PlaybackInfo playbackInfo) { playbackInfoList.delete(playbackInfo); firePlaybackInfosListener(PlayBackEvent.Action.START_REPLAY, playbackInfo); } + @Override public PlaybackInfo[] getPlaybackInfos() { return playbackInfoList.getList(); } + @Override public PlaybackInfo getPlaybackInfo(ElementId elementId) { return (PlaybackInfo) playbackInfoList.get(elementId); } + @Override public PlaybackManager getPlaybackManager() { return playbackManager; } + @Override public void setPlaybackManager(PlaybackManager playbackManager) { this.playbackManager = playbackManager; } + @Override public Account autoRegisterTeam(String displayName, String[] memberNames, String password) { Account account = accountList.assignNewTeam (siteNumber, displayName, memberNames, password); addAccount(account); return account; } + @Override public EventFeedDefinition[] getEventFeedDefinitions() { return eventFeedDefinitionsList.getList(); } + @Override public void addEventFeedDefinition(EventFeedDefinition eventFeedDefinition) { eventFeedDefinitionsList.add(eventFeedDefinition); @@ -3236,18 +3478,21 @@ public void addEventFeedDefinition(EventFeedDefinition eventFeedDefinition) { fireEventFeedDefinitionListener(eventFeedDefinitionEvent); } + @Override public void deleteEventFeedDefinition(EventFeedDefinition eventFeedDefinition) { eventFeedDefinitionsList.delete(eventFeedDefinition); EventFeedDefinitionEvent eventFeedDefinitionEvent = new EventFeedDefinitionEvent(EventFeedDefinitionEvent.Action.DELETED, eventFeedDefinition); fireEventFeedDefinitionListener(eventFeedDefinitionEvent); } + @Override public void updateEventFeedDefinition(EventFeedDefinition eventFeedDefinition) { eventFeedDefinitionsList.update(eventFeedDefinition); EventFeedDefinitionEvent problemEvent = new EventFeedDefinitionEvent(EventFeedDefinitionEvent.Action.CHANGED, eventFeedDefinition); fireEventFeedDefinitionListener(problemEvent); } + @Override public EventFeedDefinition getEventFeedDefinition(ElementId elementId) { return (EventFeedDefinition) eventFeedDefinitionsList.get(elementId); } @@ -3257,7 +3502,7 @@ public void addLanguages(Language[] languages) { for (Language language : languages) { languageDisplayList.add(language); languageList.add(language); - } + } LanguageEvent languageEvent = new LanguageEvent(LanguageEvent.Action.ADDED_LANGUAGES, languages); fireLanguageListener(languageEvent); } @@ -3291,12 +3536,12 @@ public void updateGroups(Group[] groups) { GroupEvent groupEvent = new GroupEvent(GroupEvent.Action.CHANGED_GROUPS, groups); fireGroupListener(groupEvent); } - + @Override public boolean isCommandLineOptionPresent(String optionName) { return parseArguments.isOptPresent(optionName); } - + @Override public void setCommandLineArguments(ParseArguments aParseArguments) { this.parseArguments = aParseArguments; diff --git a/src/edu/csus/ecs/pc2/core/model/Problem.java b/src/edu/csus/ecs/pc2/core/model/Problem.java index d15d1e6b2..9ef3321c8 100644 --- a/src/edu/csus/ecs/pc2/core/model/Problem.java +++ b/src/edu/csus/ecs/pc2/core/model/Problem.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 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.core.model; import java.io.File; @@ -21,14 +21,14 @@ /** * Problem Definition. - * + * * This contains settings for a problem. Data files * are not in this class, data files are in the {@link edu.csus.ecs.pc2.core.model.ProblemDataFiles} * class. - * + * * @see edu.csus.ecs.pc2.core.list.ProblemList * @see edu.csus.ecs.pc2.core.model.ProblemDataFiles - * + * * @author pc2@ecs.csus.edu */ public class Problem implements IElementObject { @@ -42,11 +42,11 @@ public class Problem implements IElementObject { * Zero indicates no per-problem limit has been set (and therefore the current * global setting should be used). */ - public static final int DEFAULT_MAX_OUTPUT_FILE_SIZE_KB = 0; + public static final int DEFAULT_MAX_OUTPUT_FILE_SIZE_KB = 0; + - public static final int DEFAULT_MEMORY_LIMIT_MB = 0 ; //zero memory limit = "none", i.e. the problem can use all available memory - + public static final SandboxType DEFAULT_SANDBOX_TYPE = SandboxType.NONE ; /** @@ -66,29 +66,29 @@ public class Problem implements IElementObject { /** * Judge's data file name. - * + * * This is the name of the file that the submitted run will read from. */ private String judgesInputDataFileName = null; /** * Judge's answer file name. - * + * * This is the name of the file that the validator will read/use to compare against the submitted run output. */ private String answerFileName = null; - + /** * List of judge's file names, for multiple test cases. - * + * */ private String [] testCaseDataFilenames = new String[0]; - + /** * List of judge's answer file names, for multiple test cases. */ private String [] testCaseAnswerFilenames = new String[0];; - + /** * Whether or not the problem should be shown to teams. */ @@ -103,7 +103,7 @@ public class Problem implements IElementObject { * Seconds per problem run. */ private int timeOutInSeconds = DEFAULT_TIMEOUT_SECONDS; - + /** * Maximum output allowed to be produced by a solution for this problem. * Note that a value of zero indicates that this problem does not have its own @@ -118,41 +118,41 @@ public enum VALIDATOR_TYPE { /** * The Problem has no associated Output Validator; it is not a Validated Problem. */ - NONE, + NONE, /** * The Problem uses the PC2 Validator, also known as the "Internal" Validator. */ - PC2VALIDATOR, + PC2VALIDATOR, /** * The Problem uses the PC2 implementation of the CLICS Validator. */ - CLICSVALIDATOR, + CLICSVALIDATOR, /** * The Problem uses a Custom (user-provided) Output Validator. */ CUSTOMVALIDATOR } - + //The type of output validator associated with this Problem. private VALIDATOR_TYPE validatorType = VALIDATOR_TYPE.NONE ; - + //the settings for each possible type of output validator used by the problem private PC2ValidatorSettings pc2ValidatorSettings ; private ClicsValidatorSettings clicsValidatorSettings ; private CustomValidatorSettings customValidatorSettings ; - + /** * If true, when loading data sets, load sample data sets before loading other judge's test data sets. */ private boolean loadDataFilesSamplesFirst = false; - + /** * This enum defines the types of Input Validators which a Problem can have. - * + * * Note that it is possible to use more than one Input Validator to check a problem's data files; * however, each problem when it is saved on the PC2 server has a single "current Input Validator" * type associated with it at that time - specifically, the most recently-selected Input Validator type. - * + * * Note also that it is possible to SELECT an Input Validator (thus making that Input Validator type * the "currently selected Input Validator type") without actually RUNNING the selected Input Validator. * Clients should use {@link #getCurrentInputValidatorType()} to determine the most recently selected @@ -162,30 +162,30 @@ public enum VALIDATOR_TYPE { * SELECTED Input Validator). Methods {@link #isVivaInputValidatorHasBeenRun()} and * {@link #isCustomInputValidatorHasBeenRun()} can be used to determine whether the corresponding Input Validator * has actually been executed. - * + * * @see #getCurrentInputValidatorType() * @see #isProblemHasCustomInputValidator() * @see #isProblemHasVivaInputValidatorPattern() * @see #isVivaInputValidatorHasBeenRun() * @see #isCustomInputValidatorHasBeenRun() - * + * * @author John Clevenger, PC2 Development Team (pc2@ecs.csus.edu) */ public enum INPUT_VALIDATOR_TYPE { /** * The Problem has no associated Input Validator. */ - NONE, + NONE, /** * The Problem uses the VIVA Input Validator. */ - VIVA, + VIVA, /** * The Problem uses a Custom (user-provided) Input Validator. */ CUSTOM } - + /** * This enum defines the possible Input Validation Status values which a problem may have. * @author John Clevenger, PC2 Development Team (pc2@ecs.csus.edu) @@ -203,29 +203,29 @@ public enum InputValidationStatus { /** * An Input Validator has been run on the data files in this problem and they all passed. */ - PASSED, + PASSED, /** * An Input Validator has been run on the data files in this problem and one or more data files failed. */ - FAILED, + FAILED, /** * An internal error occurred. One reason for this could be that an attempt was made to run an Input Validator * but it failed. */ ERROR } - + /** * The type of Input Validator currently associated with the problem. */ private INPUT_VALIDATOR_TYPE currentInputValidatorType = INPUT_VALIDATOR_TYPE.NONE; - + //custom input validator settings - only relevant if the problem was saved with a Custom Input Validator private boolean problemHasCustomInputValidator = false; private boolean customInputValidatorHasBeenRun = false; // private String customInputValidatorProgramName = ""; //program name should be determined by the Custom Input Validator Serialized File private String customInputValidatorCommandLine = ""; -// private String customInputValidatorFilesOnDiskFolderName = ""; +// private String customInputValidatorFilesOnDiskFolderName = ""; private SerializedFile customInputValidatorSerializedFile = null; private InputValidationStatus customInputValidationStatus = InputValidationStatus.UNKNOWN; private Vector customInputValidationResults = null; @@ -243,7 +243,7 @@ public enum InputValidationStatus { * This is the command executed before the run is executed. */ private String executionPrepCommand = null; - + /** * Display validation output window to judges. */ @@ -253,10 +253,10 @@ public enum InputValidationStatus { * Hide Output window from Judges. */ private boolean hideOutputWindow = false; - + /** * Show PC2 Compare Window?. - * + * */ private boolean showCompareWindow = false; @@ -264,21 +264,21 @@ public enum InputValidationStatus { * should the problem be "Auto Judged", this will require a validator be defined. */ private boolean computerJudged = false; - + /** * Should the problem be send to a human for review after it has been autojudged. * only used if computerJudged is TRUE */ private boolean manualReview = false; - + /** * should a team be notified of the Computer Judgement (immediately) * only used if manualReview is TRUE */ private boolean prelimaryNotification = false; - + private String shortName = ""; - + private String letter = null; private String colorName; @@ -286,43 +286,43 @@ public enum InputValidationStatus { private String colorRGB; /** - * Files are not stored on pc2 server, they are at an external location + * Files are not stored on pc2 server, they are at an external location * pointed to by {@link #getDataLoadYAMLPath()}. */ private boolean usingExternalDataFiles = false; - + /** * Base location where external data files are stored. */ private String externalDataFileLocation = null; - + /** * Problem State. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ - + // $HeadURL$ public enum State { /** * Active. */ - ENABLED, + ENABLED, /** * Not accepting runs */ - PAUSED, + PAUSED, /** * Not accepting runs. */ - DISABLED + DISABLED } - + private State state = State.ENABLED; - + /** - * A flag indicating whether, during execution of a submission for this problem, + * A flag indicating whether, during execution of a submission for this problem, * execution should be terminated on the first failed test case (or rather, all test cases * should be executed even when some have failed.) */ @@ -330,11 +330,11 @@ public enum State { /** * A list of groups that can view/use this problem. - * + * * One use is to limit which teams can view a group. */ - private List groups = new ArrayList(); - + private List groups = new ArrayList(); + /** * Fields related to Sandbox support. */ @@ -342,30 +342,30 @@ public enum SandboxType { /** * No sandbox being used. */ - NONE, + NONE, /** * Using the PC2 Internal sandbox. */ - PC2_INTERNAL_SANDBOX, + PC2_INTERNAL_SANDBOX, /** * Using an external (user-defined) sandbox. */ - EXTERNAL_SANDBOX + EXTERNAL_SANDBOX } - + private int memoryLimitMB = DEFAULT_MEMORY_LIMIT_MB; private SandboxType sandboxType = DEFAULT_SANDBOX_TYPE; private String sandboxCmdLine = null; private String sandboxProgramName = null; - + /** * For interactive problems - this is a read-only property, so, no mutator */ private static final String interactiveCommandLine = Constants.PC2_INTERACTIVE_COMMAND_LINE; - + /** * Create a problem with the display name. - * + * * @param displayName */ public Problem(String displayName) { @@ -386,7 +386,7 @@ public Problem copy(String newDisplayName) { Problem clone = new Problem(newDisplayName); // inherited field clone.setSiteNumber(getSiteNumber()); - + // local fields clone.setDisplayName(newDisplayName); // TODO is number really used? @@ -398,10 +398,10 @@ public Problem copy(String newDisplayName) { clone.setReadInputDataFromSTDIN(isReadInputDataFromSTDIN()); clone.setTimeOutInSeconds(getTimeOutInSeconds()); clone.setMaxOutputSizeKB(getMaxOutputSizeKB()); - + //output validator settings clone.setValidatorType(this.getValidatorType()); - + if (this.getPC2ValidatorSettings()!=null) { clone.setPC2ValidatorSettings(this.getPC2ValidatorSettings().clone()); } else { @@ -417,26 +417,26 @@ public Problem copy(String newDisplayName) { } else { clone.setCustomOutputValidatorSettings(null); } - + //input validator settings clone.setCurrentInputValidatorType(this.getCurrentInputValidatorType()); - + clone.setProblemHasCustomInputValidator(this.isProblemHasCustomInputValidator()); clone.setCustomInputValidatorHasBeenRun(this.isCustomInputValidatorHasBeenRun()); clone.setCustomInputValidationStatus(this.getCustomInputValidationStatus()); clone.setCustomInputValidatorCommandLine(StringUtilities.cloneString(this.getCustomInputValidatorCommandLine())); clone.setCustomInputValidatorFile(this.getCustomInputValidatorSerializedFile()); // clone.setInputValidatorFilesOnDiskFolder(this.getInputValidatorFilesOnDiskFolder()); - + //This statement is commented out because there is no longer a separate "problemHasVivaInputValidatorPattern" flag in the Problem class; // having a Viva pattern (or not) is determined by the value in the Pattern field in the (VivaInputValidatorSettings for the) Problem. - // This was done to avoid the possibility of an "invalid state" where a user sets "problemHasVivaInputValidator" to (say) false after + // This was done to avoid the possibility of an "invalid state" where a user sets "problemHasVivaInputValidator" to (say) false after // having set a non-zero-length pattern in the Problem. // clone.setProblemHasVivaInputValidatorPattern(this.isProblemHasVivaInputValidatorPattern()); clone.setVivaInputValidatorHasBeenRun(this.isVivaInputValidatorHasBeenRun()); clone.setVivaInputValidationStatus(this.getVivaInputValidationStatus()); clone.setVivaInputValidatorPattern(this.getVivaInputValidatorPattern()); - + //input validator results (which might be empty) Iterable inputVResults = this.getVivaInputValidatorResults(); for (InputValidationResult ivr : inputVResults) { @@ -446,7 +446,7 @@ public Problem copy(String newDisplayName) { for (InputValidationResult ivr : inputVResults) { clone.addCustomInputValidationResult(ivr); } - + clone.setInternationalJudgementReadMethod(isInternationalJudgementReadMethod()); // TODO Implement Commands to be executed before a problem is run @@ -461,10 +461,10 @@ public Problem copy(String newDisplayName) { clone.setPrelimaryNotification(isPrelimaryNotification()); clone.letter = StringUtilities.cloneString(letter); clone.shortName = StringUtilities.cloneString(shortName); - + clone.externalDataFileLocation = StringUtilities.cloneString(getExternalDataFileLocation()); clone.usingExternalDataFiles = usingExternalDataFiles; - + if (getNumberTestCases() > 1){ for (int i = 0 ; i < getNumberTestCases(); i++){ String datafile = StringUtilities.cloneString(getDataFileName(i + 1)); @@ -472,17 +472,17 @@ public Problem copy(String newDisplayName) { clone.addTestCaseFilenames(datafile, answerfile); } } - + clone.setColorName(getColorName()); clone.setColorRGB(getColorRGB()); for (Group group : groups) { clone.addGroup(group); } - + clone.setSandboxType(this.getSandboxType()); clone.setMemoryLimitMB(this.getMemoryLimitMB()); - + return clone; } @@ -490,6 +490,7 @@ public Problem copy(String newDisplayName) { /** * @see Object#equals(java.lang.Object). */ + @Override public boolean equals(Object obj) { if (this==obj) { return true; @@ -505,20 +506,21 @@ public boolean equals(Object obj) { } } - + /** * Output the title for the problem. */ + @Override public String toString() { return displayName; } - + /** * Output details of the problem. */ public String toStringDetails() { String retStr = "Problem["; - + //basic configuration settings retStr += "displayName=" + displayName; retStr += "; elementId=" + elementId; @@ -530,7 +532,7 @@ public String toStringDetails() { retStr += "; active=" + active; retStr += "; readInputDataFromSTDIN=" + readInputDataFromSTDIN; retStr += "; timeOutInSeconds=" + timeOutInSeconds; - + //output validator settings boolean validatedProblem = getValidatorType()==VALIDATOR_TYPE.NONE; retStr += "; validatedProblem=" + validatedProblem; @@ -538,21 +540,21 @@ public String toStringDetails() { retStr += "; pc2ValidatorSettings=" + getPC2ValidatorSettings(); retStr += "; clicsValidatorSettings=" + getClicsValidatorSettings(); retStr += "; customValidatorSettings=" + getCustomOutputValidatorSettings(); - + //input validator settings retStr += "; currentInputValidatorType=" + getCurrentInputValidatorType(); retStr += "; problemHasVivaInputValidatorPattern=" + isProblemHasVivaInputValidatorPattern(); retStr += "; vivaInputValidationStatus=" + getVivaInputValidationStatus(); retStr += "; problemHasCustomInputValidator=" + isProblemHasCustomInputValidator(); retStr += "; customInputValidationStatus=" + getCustomInputValidationStatus(); - + SerializedFile customInputValidatorFile = getCustomInputValidatorSerializedFile(); String customInputValidatorName = ""; if (customInputValidatorFile!=null) { customInputValidatorName = customInputValidatorFile.getName(); } retStr += "; customInputValidatorProgramName=" + customInputValidatorName; - + retStr += "; customInputValidatorCommandLine=" + customInputValidatorCommandLine; //misc additional settings @@ -571,12 +573,12 @@ public String toStringDetails() { retStr += "; usingExternalDataFiles=" + usingExternalDataFiles; retStr += "; externalDataFileLocation=" + externalDataFileLocation; retStr += "; state=" + state; - + retStr += "; sandboxType=" + this.getSandboxType(); retStr += "; sandboxCmdLine=" + this.getSandboxCmdLine(); retStr += "; sandboxProgramName=" + this.getSandboxProgramName(); retStr += "; memoryLimit=" + this.getMemoryLimitMB(); - + retStr += "; interactiveCommandLine=" + this.getInteractiveCommandLine(); retStr += "]"; @@ -586,6 +588,7 @@ public String toStringDetails() { /** * @return Returns the elementId. */ + @Override public ElementId getElementId() { return elementId; } @@ -619,7 +622,7 @@ public boolean isActive() { public String getAnswerFileName() { return answerFileName; } - + public String getAnswerFileName(int testCaseNumber) { if (testCaseNumber == 1 && testCaseAnswerFilenames.length == 0){ return answerFileName; @@ -637,9 +640,9 @@ public String getDataFileName() { /** * Get test case data file name. - * + * * Test case numbers start at 1. - * + * * @return returns data file name for test case. */ public String getDataFileName(int testCaseNumber) { @@ -682,7 +685,7 @@ public int getTimeOutInSeconds() { * Returns the problem-specific maximum allowed output file size, in KB. * A returned value of zero indicates that the problem has no problem-specific output size limit, * in which case the value of the global output size limit should be used instead. - * + * * @return the problem-specific maximum allowed output size limit in KB. */ public long getMaxOutputSizeKB() { @@ -692,8 +695,8 @@ public long getMaxOutputSizeKB() { /** * Set the maximum output size (in KB) allowed by this problem. * A value of zero (which is the default) indicates that no problem-specific output size limit - * has been set and that the global value (as returned by {@link IInternalContest#getContestInformation()}) should be used. - * + * has been set and that the global value (as returned by {@link IInternalContest#getContestInformation()}) should be used. + * * @param maxOutputSizeKB the maximum output size, in KB, to set for this problem. */ public void setMaxOutputSizeKB(long maxOutputSizeKB) { @@ -704,30 +707,30 @@ public void setMaxOutputSizeKB(long maxOutputSizeKB) { * Returns the state variable indicating what type of Validator this Problem is using. * The returned value will be an element of the enumerated type {@link edu.csus.ecs.pc2.core.Problem.VALIDATOR_TYPE}; * note that this enumeration includes "NONE" to indicate that a Problem has no Validator attached. - * + * * @see {@link edu.csus.ecs.pc2.core.Problem.VALIDATOR_TYPE} * @see {@link #isValidatedProblem()} */ public VALIDATOR_TYPE getValidatorType() { return this.validatorType; } - + /** * Sets the state variable indicating what type of Validator this problem is using. * Note that one possible value of this variable is "NONE", indicating the Problem is not validated. - * + * * @see #isValidatedProblem() - * - * @param valType a {@link edu.csus.ecs.pc2.core.model.Problem.VALIDATOR_TYPE} indicating the + * + * @param valType a {@link edu.csus.ecs.pc2.core.model.Problem.VALIDATOR_TYPE} indicating the * type of validator used by this Problem */ public void setValidatorType(VALIDATOR_TYPE valType) { this.validatorType = valType; } /** - * Returns whether the Problem is using the PC2Validator (as opposed to a Custom Validator, - * the CLICS Validator, or no Validator). - * + * Returns whether the Problem is using the PC2Validator (as opposed to a Custom Validator, + * the CLICS Validator, or no Validator). + * * @return true if the Problem is using the PC2Validator */ public boolean isUsingPC2Validator() { @@ -735,9 +738,9 @@ public boolean isUsingPC2Validator() { } /** - * Returns whether this Problem is using the CLICS Validator (as opposed to a Custom Validator, - * the PC2Validator, or no Validator). - * + * Returns whether this Problem is using the CLICS Validator (as opposed to a Custom Validator, + * the PC2Validator, or no Validator). + * * @return true if the Problem is using the CLICS Validator */ public boolean isUsingCLICSValidator() { @@ -745,9 +748,9 @@ public boolean isUsingCLICSValidator() { } /** - * Returns whether this Problem is using a Custom (user-supplied) Validator + * Returns whether this Problem is using a Custom (user-supplied) Validator * (as opposed to the CLICS Validator, the PC2Validator, or no Validator). - * + * * @return true if the Problem is using a Custom validator */ public boolean isUsingCustomValidator() { @@ -764,10 +767,10 @@ public boolean isValidatedProblem() { /** * Returns the Output Validator Command Line associated with this Problem, if the Problem is using an Output Validator; * returns null otherwise. - * - * @return the validatorCommandLine for the Problem's output validator, + * + * @return the validatorCommandLine for the Problem's output validator, * or null if the Problem is not using an Output Validator. - * + * * @throws {@link RuntimeException} if the Problem is marked as using an Output Validator but no corresponding Validator * Settings could be found. */ @@ -775,7 +778,7 @@ public String getOutputValidatorCommandLine() { if (!isValidatedProblem()) { return null; } - + //search for ValidatorSettings for the currently-specified Validator; if found, return the ValidatorCommandLine // from those Settings String validatorCommandLine = null; @@ -796,10 +799,10 @@ public String getOutputValidatorCommandLine() { found = true; } } - + if (!found) { throw new RuntimeException("getValidatorCommandLine(): unable to locate Settings for currently-specified Validator '" - + getValidatorType() + "'"); + + getValidatorType() + "'"); } else { return validatorCommandLine; } @@ -808,24 +811,24 @@ public String getOutputValidatorCommandLine() { /** * Sets the Output Validator Command Line associated with the type of Output Validator configured for this Problem. * Note that this Problem class does not maintain a separate "Output Validator Command Line" field; - * rather, the current Output Validator Command Line is always stored within a "Settings" object + * rather, the current Output Validator Command Line is always stored within a "Settings" object * corresponding to the currently-assigned Validator type. - * + * * @param commandLine the new command line for the currently-specified Output Validator type associated with the Problem - * + * * @see PC2ValidatorSettings * @see ClicsValidatorSettings * @see CustomValidatorSettings - * + * * @throws {@link RuntimeException} if the Problem is not marked as using a Validator, or is marked as using a Validator * but no corresponding Validator Settings object could be found. */ public void setOutputValidatorCommandLine(String commandLine) { - + if (!isValidatedProblem()) { - throw new RuntimeException("setValidatorCommandLine(): no Validator configured for Problem"); + throw new RuntimeException("setValidatorCommandLine(): no Validator configured for Problem"); } - + //search for ValidatorSettings for the currently-specified Validator; if found, set the ValidatorCommandLine // into those Settings boolean found = false; @@ -845,10 +848,10 @@ public void setOutputValidatorCommandLine(String commandLine) { found = true; } } - + if (!found) { - throw new RuntimeException("setValidatorCommandLine(): unable to locate Settings for currently-specified Validator"); - } + throw new RuntimeException("setValidatorCommandLine(): unable to locate Settings for currently-specified Validator"); + } } /** @@ -932,9 +935,9 @@ public void setHideOutputWindow(boolean hideOutputWindow) { /** * Returns the Output Validator Program Name if the Problem has an Output Validator attached; otherwise returns null. - * + * * @return the validatorProgramName if there is an Output Validator for the Problem, or null if not - * + * * @throws {@link RuntimeException} if the Problem is marked as having an Output Validator but no Validator Settings could be found */ public String getOutputValidatorProgramName() { @@ -976,23 +979,23 @@ public String getOutputValidatorProgramName() { * Note that this Problem class does not maintain a separate "Validator Program Name" field; * rather, the Output Validator Program Name is stored within the "Settings" object associated with the * type of Output Validator attached to the Problem. - * + * * @param validatorProgramName a String specifying the new Output Validator Program Name - * + * * @see PC2ValidatorSettings * @see ClicsValidatorSettings * @see CustomValidatorSettings - * + * * @throws {@link RuntimeException} if the Problem is not marked as having an Output Validator when an attempt is made to set - * set the Output Validator Program name, or if the Problem is marked as having an Output Validator but no Validator Settings + * set the Output Validator Program name, or if the Problem is marked as having an Output Validator but no Validator Settings * object could be found */ public void setOutputValidatorProgramName(String validatorProgramName) { - + if (!this.isValidatedProblem()) { throw new RuntimeException("Cannot set a Validator Program Name on a Problem marked as not using a Validator"); } - + // search for ValidatorSettings for the currently-specified Validator; if found, set the ValidatorProgramName // into those Settings boolean found = false; @@ -1015,7 +1018,7 @@ public void setOutputValidatorProgramName(String validatorProgramName) { if (!found) { throw new RuntimeException("setValidatorProgramName(): unable to locate Settings for currently-specified Validator"); - } + } } /** @@ -1034,22 +1037,25 @@ protected void setNumber(int number) { this.number = number; } + @Override public int versionNumber() { return elementId.getVersionNumber(); } + @Override public int getSiteNumber() { return elementId.getSiteNumber(); } + @Override public void setSiteNumber(int siteNumber) { elementId.setSiteNumber(siteNumber); } /** * Returns an indication of which option has been selected when using the PC2Validator. - * - * @return an integer indicating which PC2Validator option has been specified, + * + * @return an integer indicating which PC2Validator option has been specified, * or -1 if no PC2Validator Settings for the Problem could be found */ public int getWhichPC2Validator() { @@ -1063,13 +1069,13 @@ public int getWhichPC2Validator() { /** * Sets the value indicating which option has been selected when using the PC2Validator. - * + * * @param whichPC2Validator -- the integer value to which the PC2Validator option should be set - * + * * @throws {@link RuntimeException} if there is no PC2 Validator Settings object attached to the Problem */ public void setWhichPC2Validator(int whichPC2Validator) { - + if (getPC2ValidatorSettings()!=null) { getPC2ValidatorSettings().setWhichPC2Validator(whichPC2Validator); } else { @@ -1077,10 +1083,11 @@ public void setWhichPC2Validator(int whichPC2Validator) { } } + @Override public int hashCode() { return getElementId().toString().hashCode(); } - + public boolean isSameAs(Problem otherProblem) { try { @@ -1106,7 +1113,7 @@ public boolean isSameAs(Problem otherProblem) { if (!readInputDataFromSTDIN == otherProblem.isReadInputDataFromSTDIN()) { return false; } - + if (this.isValidatedProblem() != otherProblem.isValidatedProblem()) { return false; } @@ -1147,20 +1154,20 @@ public boolean isSameAs(Problem otherProblem) { return false; } } - + //check general Input Validation settings if (this.getVivaInputValidationStatus() != otherProblem.getVivaInputValidationStatus()) { return false; } - + if (this.getCustomInputValidationStatus() != otherProblem.getCustomInputValidationStatus()) { return false; } - + if (this.getCurrentInputValidatorType() != otherProblem.getCurrentInputValidatorType()) { return false; } - + //check for differences in Custom Input Validator settings if (this.isProblemHasCustomInputValidator() != otherProblem.isProblemHasCustomInputValidator()) { return false; @@ -1174,9 +1181,9 @@ public boolean isSameAs(Problem otherProblem) { if (!this.isCustomInputValidatorHasBeenRun()==otherProblem.isCustomInputValidatorHasBeenRun()) { return false; } - + //check for differences in Custom Input Validator results? - + //check for differences in Viva Input Validator settings if (!Arrays.equals(this.getVivaInputValidatorPattern(), otherProblem.getVivaInputValidatorPattern())) { return false; @@ -1184,11 +1191,11 @@ public boolean isSameAs(Problem otherProblem) { if (!this.isVivaInputValidatorHasBeenRun()==otherProblem.isVivaInputValidatorHasBeenRun()) { return false; } - + if (showValidationToJudges != otherProblem.isShowValidationToJudges()) { return false; } - + if (hideOutputWindow != otherProblem.isHideOutputWindow()) { return false; } @@ -1204,19 +1211,19 @@ public boolean isSameAs(Problem otherProblem) { if (prelimaryNotification != otherProblem.isPrelimaryNotification()) { return false; } - + if (getSiteNumber() != otherProblem.getSiteNumber()){ return false; } - + if (! StringUtilities.stringSame(shortName, otherProblem.getShortName())){ return false; } - + if (! StringUtilities.stringSame(externalDataFileLocation, otherProblem.getExternalDataFileLocation())){ return false; } - + if (usingExternalDataFiles != otherProblem.usingExternalDataFiles) { return false; } @@ -1231,44 +1238,44 @@ public boolean isSameAs(Problem otherProblem) { // if (! StringUtilities.stringArraySame(testCaseAnswerFilenames, problem.getTestCaseAnswerFilenames())) { // return false; // } - + if (!this.isStopOnFirstFailedTestCase() == otherProblem.isStopOnFirstFailedTestCase()) { return false; } - + if (! StringUtilities.stringSame(colorName, otherProblem.getColorName())){ return false; } - + if (! StringUtilities.stringSame(colorRGB, otherProblem.getColorRGB())){ return false; } - + if (! StringUtilities.stringSame(colorRGB, otherProblem.getColorRGB())){ return false; } - + if ( ! groups.equals(otherProblem.getGroups())){ return false; } - + if (! (this.getMaxOutputSizeKB() == otherProblem.getMaxOutputSizeKB()) ) { return false; } - + //check for equivalence in Sandbox configuration if (this.getSandboxType() != otherProblem.getSandboxType()) { return false ; - } - + } + //check for same memory limits if (this.getMemoryLimitMB() != otherProblem.getMemoryLimitMB()) { return false; } - + //all comparisons pass; problems are equivalent return true; - + } catch (Exception e) { StaticLog.getLog().log(Log.WARNING, "Exception comparing Problem "+e.getMessage(), e); e.printStackTrace(System.err); @@ -1314,7 +1321,7 @@ public boolean isInteractive() { /** * Get short name for problem. - * + * * @return */ public String getShortName() { @@ -1324,12 +1331,12 @@ public String getShortName() { public void setShortName(String shortName) { this.shortName = shortName; } - + /** * Valid short name?.
    - * + * * A valid short name does not contain path or drive delimiters, these symbols : / \ - * + * * @param name * @return true if valid, false otherwise. */ @@ -1342,10 +1349,10 @@ public boolean isValidShortName(String name) { public boolean isValidShortName() { return isValidShortName(shortName); } - + /** * Get letter for problem. - * + * * @return */ public String getLetter() { @@ -1355,30 +1362,30 @@ public String getLetter() { public void setLetter(String letter) { this.letter = letter; } - + /** * Add data and answer filenames to list of test cases. - * + * * @see #removeAllTestCaseFilenames() * @param datafile * @param answerfile */ public void addTestCaseFilenames (String datafile, String answerfile){ - + String[] newArray; if (datafile != null) { newArray = StringUtilities.appendString(testCaseDataFilenames, datafile); testCaseDataFilenames = newArray; } - + if (answerfile != null){ newArray = StringUtilities.appendString(testCaseAnswerFilenames, answerfile); testCaseAnswerFilenames = newArray; } } - - + + /** * Remove all test case filenames. */ @@ -1386,7 +1393,7 @@ public void removeAllTestCaseFilenames(){ testCaseDataFilenames = new String[0]; testCaseAnswerFilenames = new String[0]; } - + public int getNumberTestCases() { // this needs to look at the longest of the testCase Data and Answer Filesnames lists if (testCaseDataFilenames != null && testCaseDataFilenames.length > 0) { @@ -1413,7 +1420,7 @@ public void setExecutionPrepCommand(String executionPrepCommand) { public void setColorName(String colorName) { this.colorName = colorName; } - + public String getColorName() { return colorName; } @@ -1421,12 +1428,12 @@ public String getColorName() { public void setColorRGB(String colorRGB) { this.colorRGB = colorRGB; } - + public String getColorRGB() { return colorRGB; } - + /** * @return true if not storing files on pc2 server. */ @@ -1443,24 +1450,24 @@ public void setUsingExternalDataFiles(boolean usingExternalDataFiles) { /** * Local file path for external data files. - * + * * @param dataLoadYAMLPath */ public void setExternalDataFileLocation(String externalDataFileLocation) { this.externalDataFileLocation = externalDataFileLocation; } - + public String getExternalDataFileLocation() { return externalDataFileLocation; } - + /** * Return external judges data file (location). - * + * * Searches both the dir found in {@link #getExternalDataFileLocation()} or * in that location plus the CCS standard location for that file. - * - * @param dataSetNumber + * + * @param dataSetNumber * @return null if not found, else the path for the file. */ public File locateJudgesDataFile (int dataSetNumber){ @@ -1469,20 +1476,20 @@ public File locateJudgesDataFile (int dataSetNumber){ /** * Return external judges answer file (location). - * + * * Searches both the dir found in {@link #getExternalDataFileLocation()} or * in that location plus the CCS standard location for that file. - * + * * @param dataSetNumber * @return */ public File locateJudgesAnswerFile (int dataSetNumber){ return locateDataFile(getDataFileName(dataSetNumber)); } - + /** * Get external CCS standard location for data files. - * + * * @return */ public String getCCSfileDirectory() { @@ -1492,7 +1499,7 @@ public String getCCSfileDirectory() { return getExternalDataFileLocation() + File.separator + "data" + File.separator + "secret"; } } - + private File locateDataFile(String filename) { String directoryName = getExternalDataFileLocation(); if (directoryName == null) { @@ -1534,11 +1541,11 @@ private File locateDataFile(String filename) { // private String[] getTestCaseAnswerFilenames() { // return testCaseAnswerFilenames; // } - + public State getState() { return state; } - + public void setState(State state) { this.state = state; } @@ -1549,7 +1556,7 @@ public void setElementId(Problem problem) { /** * Returns the current {@link PC2ValidatorSettings} object attached to this Problem. - * + * * @return the current PC2ValidatorSettings object */ public PC2ValidatorSettings getPC2ValidatorSettings() { @@ -1558,7 +1565,7 @@ public PC2ValidatorSettings getPC2ValidatorSettings() { /** * Sets the {@link PC2ValidatorSettings} for this Problem to the specified settings object. - * + * * @param settings the PC2ValidatorSettings object to attach to this Problem */ public void setPC2ValidatorSettings(PC2ValidatorSettings settings) { @@ -1568,7 +1575,7 @@ public void setPC2ValidatorSettings(PC2ValidatorSettings settings) { /** * Returns a {@link ClicsValidatorSettings} object containing the options which this * Problem should apply when using the CLICS validator. - * + * * @return the clicsValidatorSettings for this problem */ public ClicsValidatorSettings getClicsValidatorSettings() { @@ -1577,7 +1584,7 @@ public ClicsValidatorSettings getClicsValidatorSettings() { /** * Sets the {@link ClicsValidatorSettings} for this problem to the specified value. - * + * * @param settings the CLICS Validator Settings to set */ public void setCLICSValidatorSettings(ClicsValidatorSettings settings) { @@ -1602,10 +1609,10 @@ public void setCustomOutputValidatorSettings(CustomValidatorSettings settings) { } //*** Methods associated with Custom Input Validators *** - + /** * Returns an indication of whether or not this Problem has a Custom Input Validator attached. - * + * * @return true if the problem has a Custom Input validator */ public boolean isProblemHasCustomInputValidator() { @@ -1614,7 +1621,7 @@ public boolean isProblemHasCustomInputValidator() { /** * Sets the flag indicating whether or not the Problem has a Custom Input Validator. - * + * * @param problemHasCustomInputValidator the value to which the flag should be set */ public void setProblemHasCustomInputValidator(boolean problemHasCustomInputValidator) { @@ -1624,15 +1631,15 @@ public void setProblemHasCustomInputValidator(boolean problemHasCustomInputValid /** * Returns the Input Validation status of the problem with respect to running a Custom Input Validator. * Note that a problem may have more than one Input Validator applied to it (e.g., a Custom Input Validator - * and/or the VIVA Input Validator); this method returns the Input Validation status determined by the + * and/or the VIVA Input Validator); this method returns the Input Validation status determined by the * execution (if any) of a Custom Input Validator. - * - * Use method {@link #getCurrentInputValidatorType()} to determine + * + * Use method {@link #getCurrentInputValidatorType()} to determine * the current type of Input Validator associated with the problem (that is, the most recently selected * Input Validator type). - * + * * @return an element of {@link InputValidationStatus} indicating the Custom Input Validation status of the problem. - * + * * @see #getCurrentInputValidatorType() * @see #getVivaInputValidationStatus() * @@ -1640,10 +1647,10 @@ public void setProblemHasCustomInputValidator(boolean problemHasCustomInputValid public InputValidationStatus getCustomInputValidationStatus() { return customInputValidationStatus; } - + /** * Sets the Custom Input Validation status of this problem to the specified value. - * + * * @param status the value to which the Custom Input Validation status for the problem should be set. */ public void setCustomInputValidationStatus (InputValidationStatus status) { @@ -1653,7 +1660,7 @@ public void setCustomInputValidationStatus (InputValidationStatus status) { /** * Returns the name of the Custom Input Validator for the Problem, or the empty string if the Problem has no * defined Custom Input Validator (that is, if the Custom Input Validator SerializedFile associated with the Problem is null). - * + * * @return the Custom Input Validator Program Name for the Problem, or an empty string */ public String getCustomInputValidatorProgramName() { @@ -1668,7 +1675,7 @@ public String getCustomInputValidatorProgramName() { // Custom Input Validator Serialized File and should not be able to be set independently of that file. // /** // * Sets the name of the Custom Input Validator program for this Problem. -// * +// * // * @param customInputValidatorProgramName the name of the Custom Input Validator program // */ // public void setCustomInputValidatorProgramName(String inputValidatorProgramName) { @@ -1679,7 +1686,7 @@ public String getCustomInputValidatorProgramName() { * Returns the Custom Input Validator command for the Problem (that is, the command used to * invoke the Custom Input Validator), or the empty string if the Problem has no * defined Custom Input Validator (that is, if the Custom Input Validator command is null or the empty string). - * + * * @return the Custom Input Validator Command Line */ public String getCustomInputValidatorCommandLine() { @@ -1692,7 +1699,7 @@ public String getCustomInputValidatorCommandLine() { /** * Sets the command line used to invoke a Custom Input Validator. - * + * * @param customInputValidatorCommandLine the customInputValidatorCommandLine to set. */ public void setCustomInputValidatorCommandLine(String inputValidatorCommandLine) { @@ -1705,30 +1712,30 @@ public void setCustomInputValidatorCommandLine(String inputValidatorCommandLine) // * Returns the name of the folder containing the input files for this Problem. // * Note that is value is only meaningful if the user has run the Input Validator // * and selected "Files on disk in folder" as the input file source. -// * +// * // */ // public String getInputValidatorFilesOnDiskFolder() { // return this.customInputValidatorFilesOnDiskFolderName; // } -// +// // /** // * Sets the value of the InputFilesOnDiskFolder variable for this problem. -// * +// * // * @param inputValFilesOnDiskFolder // */ // public void setInputValidatorFilesOnDiskFolder(String inputValFilesOnDiskFolder) { // this.customInputValidatorFilesOnDiskFolderName = inputValFilesOnDiskFolder; -// +// // } - + /** - * Returns an {@link Iterable} for the current Custom Input Validator {@link InputValidationResults} for this problem. + * Returns an {@link Iterable} for the current Custom Input Validator {@link InputValidationResults} for this problem. * The returned object may be empty (that it, the Iterable may have no elements) but will never be null. - * + * * Note that a Problem may have been tested with both the VIVA Input Validator and a user-defined Custom - * Input Validator; however, the results returned by this method will always be those generated by the - * most recent execution of the Custom Input Validator (if any). - * + * Input Validator; however, the results returned by this method will always be those generated by the + * most recent execution of the Custom Input Validator (if any). + * * @return an {@link Iterable} containing Custom Input Validator InputValidationResults, or null if no such results exist. */ public Iterable getCustomInputValidatorResults() { @@ -1737,7 +1744,7 @@ public Iterable getCustomInputValidatorResults() { } return this.customInputValidationResults; } - + /** * Returns the number of Custom Input Validator {@link InputValidationResult}s currently stored in this Problem. */ @@ -1747,13 +1754,13 @@ public int getNumCustomInputValidationResults() { } return this.customInputValidationResults.size(); } - + /** * Adds the specified {@link InputValidationResult} to the current set of Custom Input Validation Results * for the Problem. - * + * * @param result the InputValidationResult to be added. - * + * */ public void addCustomInputValidationResult(InputValidationResult result) { if (getCustomInputValidatorResults() == null) { @@ -1765,19 +1772,19 @@ public void addCustomInputValidationResult(InputValidationResult result) { /** * Clears (removes) all Custom Input Validator {@link InputValidationResults} currently stored in this Problem. * Note that calling this method does not affect any VIVA Input Validation results stored in the Problem. - * + * * @see #clearVivaInputValidationResults() */ public void clearCustomInputValidationResults() { this.customInputValidationResults = null; } - - + + //*** Methods associated with the VIVA Input Validator *** - + /** * Returns an indication of whether or not this Problem has a Viva Input Validator pattern attached. - * + * * @return true if the problem has a Viva Input validator pattern. */ public boolean isProblemHasVivaInputValidatorPattern() { @@ -1787,14 +1794,14 @@ public boolean isProblemHasVivaInputValidatorPattern() { return vivaSettings.isProblemHasVivaInputValidatorPattern(); } - //This method is commented out because there is no longer a separate "problemHasVivaInputValidatorPattern" flag in the Problem class; + //This method is commented out because there is no longer a separate "problemHasVivaInputValidatorPattern" flag in the Problem class; // having a Viva pattern (or not) is determined by the value in the Pattern field in the (VivaInputValidatorSettings for the) Problem. - // This was done to avoid the possibility of an "invalid state" where a user sets "problemHasInputValidator" to (say) false after having + // This was done to avoid the possibility of an "invalid state" where a user sets "problemHasInputValidator" to (say) false after having // set a non-zero-length pattern in the Problem. // /** // * Sets the flag indicating whether or not the Problem has a Viva Input Validator pattern. -// * +// * // * @param hasVivaPattern the value to which the flag should be set. // */ // public void setProblemHasVivaInputValidatorPattern(boolean hasVivaPattern) { @@ -1803,18 +1810,18 @@ public boolean isProblemHasVivaInputValidatorPattern() { // } // vivaSettings.setProblemHasVivaInputValidatorPattern(hasVivaPattern); // } - + /** * Returns the Input Validation status of the problem with respect to running the VIVA Input Validator. * Note that a problem may have more than one Input Validator applied to it (e.g., a Custom Input Validator - * and/or the VIVA Input Validator); this method returns the Input Validation status determined by the + * and/or the VIVA Input Validator); this method returns the Input Validation status determined by the * execution (if any) of the VIVA Input Validator. - * - * Use method {@link #getCurrentInputValidatorType()} to determine + * + * Use method {@link #getCurrentInputValidatorType()} to determine * the current type of Input Validator associated with the problem. - * + * * @return an element of {@link InputValidationStatus} indicating the VIVA Input Validation status of the problem. - * + * * @see #getCurrentInputValidatorType() * @see #getCustomInputValidationStatus() * @@ -1828,7 +1835,7 @@ public InputValidationStatus getVivaInputValidationStatus() { /** * Sets the VIVA Input Validation status of this problem to the specified value. - * + * * @param status the value to which the VIVA Input Validation status for the problem should be set. */ public void setVivaInputValidationStatus (InputValidationStatus status) { @@ -1840,13 +1847,13 @@ public void setVivaInputValidationStatus (InputValidationStatus status) { /** - * Returns an {@link Iterable} for the current VIVA {@link InputValidationResults} for this problem. + * Returns an {@link Iterable} for the current VIVA {@link InputValidationResults} for this problem. * The returned object may be empty (that it, the Iterable may have no elements) but will never be null. - * + * * Note that a Problem may have been tested with both the VIVA Input Validator and a user-defined Custom - * Input Validator; however, the results returned by this method will always be those generated by the - * most recent execution of the VIVA Input Validator. - * + * Input Validator; however, the results returned by this method will always be those generated by the + * most recent execution of the VIVA Input Validator. + * * @return an {@link Iterable} containing VIVA InputValidationResults. */ public Iterable getVivaInputValidatorResults() { @@ -1855,7 +1862,7 @@ public Iterable getVivaInputValidatorResults() { } return vivaSettings.getVivaInputValidationResults(); } - + /** * Returns the number of VIVA Input Validator {@link InputValidationResult}s currently stored in this Problem. */ @@ -1863,13 +1870,13 @@ public int getNumVivaInputValidationResults() { if (vivaSettings==null) { vivaSettings = new VivaInputValidatorSettings(); } - return vivaSettings.getNumVivaInputValidationResults(); + return vivaSettings.getNumVivaInputValidationResults(); } - + /** * Adds the specified {@link InputValidationResult} to the current set of VIVA Input Validation Results * for the Problem. - * + * * @param result the InputValidationResult to be added. */ public void addVivaInputValidationResult(InputValidationResult result) { @@ -1878,11 +1885,11 @@ public void addVivaInputValidationResult(InputValidationResult result) { } vivaSettings.addVivaInputValidationResult(result); } - + /** * Clears (removes) all VIVA {@link InputValidationResults} currently stored in this Problem. * Note that calling this method does not affect any Custom Input Validation results stored in the Problem. - * + * * @see #clearCustomInputValidationResults() */ public void clearVivaInputValidationResults() { @@ -1895,7 +1902,7 @@ public void clearVivaInputValidationResults() { /** * Returns the flag indicating whether this problem is configured to stop execution * on encountering a failed test case. - * + * * @return true if execution should stop after the first failed test case; false if execution should continue * (i.e. if all test cases should be executed even if some fail). */ @@ -1905,33 +1912,33 @@ public boolean isStopOnFirstFailedTestCase() { /** * Sets the flag indicating whether execution of submissions for this problem should stop on encountering the first failed test case. - * - * @param stopOnFirstFailedTestCase true indicates execution should stop on first failed test case; false indicates all test cases + * + * @param stopOnFirstFailedTestCase true indicates execution should stop on first failed test case; false indicates all test cases * should be executed */ public void setStopOnFirstFailedTestCase(boolean stopOnFirstFailedTestCase) { this.stopOnFirstFailedTestCase = stopOnFirstFailedTestCase; } - + /** * Set so all users can view this problem. */ public void clearGroups(){ groups = new ArrayList<>(); } - + public void setGroups(List groups) { this.groups = groups; } - + public List getGroups() { return groups; } - + public void addGroup(Group group){ groups.add(group); } - + /** * Is this group permitted to view/use this problem?. * @param group @@ -1939,9 +1946,9 @@ public void addGroup(Group group){ */ public boolean canView (Group group){ boolean view = (groups.size() == 0); - if (group != null) { + if (!view && group != null) { for (Iterator iterator = groups.iterator(); iterator.hasNext();) { - Group g2 = (Group) iterator.next(); + Group g2 = iterator.next(); if (group.getDisplayName().equals(g2.getDisplayName())) { view = true; break; @@ -1950,10 +1957,32 @@ public boolean canView (Group group){ } return (view); } - + + /** + * Are any of the groups in wantedGroups allowed to view/use this problem? + * + * @param wantedGroups + * @return true if any group can see the problem, false otherwise + */ + public boolean canView(List wantedGroups) { + // If no specific groups are assigned to this problem or we're not interested + // in specific groups, then the problem is viewable. + boolean view = (groups.size() == 0 || wantedGroups == null); + + if(!view) { + for(Group group : wantedGroups) { + view = canView(group); + if(view) { + break; + } + } + } + return(view); + } + /** * Are there no groups assigned to this problem? - * + * * @see canView * @see getGroups * @return true if no groups in list, false otherwise. @@ -1966,7 +1995,7 @@ public boolean isAllView(){ * Returns a String array containing the VIVA Input Validator pattern associated with this problem, * or null if no VIVA pattern has been assigned. * Note that the VIVA pattern is an array of String, one pattern line per array element. - * + * * @return a String [] containing the Viva pattern, or null. */ public String [] getVivaInputValidatorPattern() { @@ -1975,7 +2004,7 @@ public boolean isAllView(){ } return vivaSettings.getVivaInputValidatorPattern(); } - + /** * Sets the Viva Input Validator pattern for this problem to the specified String array. */ @@ -1985,20 +2014,20 @@ public void setVivaInputValidatorPattern(String [] pattern) { } vivaSettings.setVivaInputValidatorPattern(pattern) ; } - + /** * Returns the {@link SerializedFile} containing the Custom Input Validator assigned to this problem, * or null if no Custom Input Validator has been assigned. - * + * * @return a SerializedFile containing the Custom Input Validator assigned to this problem, or null. */ public SerializedFile getCustomInputValidatorSerializedFile() { return customInputValidatorSerializedFile ; } - + /** * Sets the {@link SerializedFile} containing the Custom Input Validator assigned to this problem. - * + * * @param inputValidatorFile a SerializedFile containing a Custom Input Validator program. */ public void setCustomInputValidatorFile(SerializedFile inputValidatorFile) { @@ -2008,19 +2037,19 @@ public void setCustomInputValidatorFile(SerializedFile inputValidatorFile) { /** * Returns the {@link INPUT_VALIDATOR_TYPE} currently associated with this problem -- * that is, the most recently selected Input Validator type. - * + * * Note that it is possible to SELECT an Input Validator (thus making that Input Validator type * the "currently selected Input Validator type") without actually RUNNING the selected Input Validator. * Clients should use this method to determine the most recently selected * Input Validator type; method {@link #isVivaInputValidatorHasBeenRun()} or * {@link #isCustomInputValidatorHasBeenRun()} can be used to determine whether the current Input Validator * (as returned by this method) has actually been executed. - * + * * @return the type of Input Validator associated with this problem. - * + * * @see #isVivaInputValidatorHasBeenRun() * @see #isCustomInputValidatorHasBeenRun() - * + * */ public INPUT_VALIDATOR_TYPE getCurrentInputValidatorType() { return currentInputValidatorType; @@ -2028,7 +2057,7 @@ public INPUT_VALIDATOR_TYPE getCurrentInputValidatorType() { /** * Sets the {@link INPUT_VALIDATOR_TYPE} associated with this problem. - * + * * @param currentInputValidatorType the type of Input Validator to be associated with this problem. */ public void setCurrentInputValidatorType(INPUT_VALIDATOR_TYPE currentInputValidatorType) { @@ -2072,26 +2101,26 @@ public void setVivaInputValidatorHasBeenRun(boolean vivaInputValidatorHasBeenRun /** * Returns the currently configured memory limit (in MB) for this Problem. * A memory limit of zero indicates "no limit". - * Note that memory limits are not enforced unless a sandbox has been selected + * Note that memory limits are not enforced unless a sandbox has been selected * on the Edit Problem GUI (or via YAML configuration). - * + * * @return */ public int getMemoryLimitMB() { return memoryLimitMB; } - + /** * Sets the memory limit for this problem. Setting a memory limit of zero indicates "no limit", * meaning that the problem is constrained only by the memory provided by the hardware, the OS, * and the specific language runtime system. - * - * Note that setting a memory limit does not automatically imply that such limit is enforced; + * + * Note that setting a memory limit does not automatically imply that such limit is enforced; * enforcing a memory limit requires selection of a problem sandbox capable of doing that. * (See {@link EditProblemSandboxPane}.) - * + * * If a value less than zero is passed in the memory limit is set to zero (unlimited). - * + * * @param memLimitInMB the memory limit for the problem, in MB; must be >= 0, where 0=unlimited. */ public void setMemoryLimitMB(int memLimitInMB) { @@ -2120,17 +2149,17 @@ private void updateSandboxInfo() sandboxProgramName = Constants.PC2_INTERNAL_SANDBOX_INTERACTIVE_NAME; sandboxCmdLine = Constants.PC2_INTERNAL_SANDBOX_INTERACTIVE_COMMAND_LINE; } else { - sandboxProgramName = Constants.PC2_INTERNAL_SANDBOX_PROGRAM_NAME; + sandboxProgramName = Constants.PC2_INTERNAL_SANDBOX_PROGRAM_NAME; sandboxCmdLine = Constants.PC2_INTERNAL_SANDBOX_COMMAND_LINE; } } } - + /** * Returns a String containing the name of the sandbox program associated with this Problem. * Note that the value returned by this method is only relevant if the value returned by * {@link #getSandboxType()} is something other than {@link SandboxType#NONE}. - * + * * @return the currently-defined sandbox program name. */ public String getSandboxProgramName() { @@ -2142,7 +2171,7 @@ public String getSandboxProgramName() { * Sets the name of the sandbox program used by this Problem. * Note that setting a sandbox program name does NOT in and of itself cause the specified sandbox to be * used; the Admin must configure/enable the sandbox using the Edit Problem dialog (or via YAML configuration). - * + * * @param sandboxProgramName the name of the sandbox program to be used by this Problem, when sandbox usage is enabled. */ public void setSandboxProgramName(String sandboxProgram) { @@ -2151,8 +2180,8 @@ public void setSandboxProgramName(String sandboxProgram) { /** * Returns the String containing the command used to invoke the sandbox configured for this problem. - * Note that the returned value is meaningless if the Problem has not been configured to use a sandbox. - * + * Note that the returned value is meaningless if the Problem has not been configured to use a sandbox. + * * @return the command line used to invoke the sandbox for this problem, when sandbox usage is enabled. */ public String getSandboxCmdLine() { @@ -2166,7 +2195,7 @@ public String getSandboxCmdLine() { * Admin must enable the sandbox via the Edit Problem dialog (or via YAML configuration). * Note also that the value of sandboxCmdLine is meaningless if the Problem is currently configured * with {@link SandboxType#NONE}. - * + * * @param sandboxCmdLine the command line used to invoke the Problem sandbox. */ public void setSandboxCmdLine(String sandboxCmdLine) { @@ -2176,7 +2205,7 @@ public void setSandboxCmdLine(String sandboxCmdLine) { /** * Returns a boolean flag which indicates whether this Problem has been configured to use a sandbox. * - * @return false if the currently configured SandboxType for the problem is {@link Problem.SandboxType#NONE}; + * @return false if the currently configured SandboxType for the problem is {@link Problem.SandboxType#NONE}; * true if any other sandbox type has been configured. */ public boolean isUsingSandbox() { @@ -2184,9 +2213,9 @@ public boolean isUsingSandbox() { } /** - * Returns the type of sandbox configured in this Problem; an element of {@link Problem.SandboxType} + * Returns the type of sandbox configured in this Problem; an element of {@link Problem.SandboxType} * which might be {@link SandboxType#NONE}. - * + * * @return an element of {@link Problem.SandboxType}. */ public SandboxType getSandboxType() { @@ -2200,16 +2229,16 @@ public SandboxType getSandboxType() { * Sets the type of sandbox being used by this Problem. If the specified type of sandbox is * {@link SandboxType#PC2_INTERNAL_SANDBOX}, also sets the Sandbox Command Line and Sandbox Program Name * to their PC2 Internal Sandbox values. - * + * * @param sandboxType the type of sandbox to be used by this Problem, which might be {@link Sandbox#NONE}. */ public void setSandboxType(SandboxType sandboxType) { this.sandboxType = sandboxType; - + //if we're setting the PC2 internal sandbox, also set the sandbox command line and program name updateSandboxInfo(); } - + /** * Gets the command line to use to run interactive problems. Currently, this is a read-only * value since it does not make sense to change it at this time. @@ -2221,7 +2250,7 @@ public String getInteractiveCommandLine() { public boolean isLoadDataFilesSamplesFirst() { return loadDataFilesSamplesFirst; } - + public void setLoadDataFilesSamplesFirst(boolean loadDataFilesSamplesFirst) { this.loadDataFilesSamplesFirst = loadDataFilesSamplesFirst; } diff --git a/src/edu/csus/ecs/pc2/core/report/AccountsReport.java b/src/edu/csus/ecs/pc2/core/report/AccountsReport.java index 201a32eb0..7a551d851 100644 --- a/src/edu/csus/ecs/pc2/core/report/AccountsReport.java +++ b/src/edu/csus/ecs/pc2/core/report/AccountsReport.java @@ -1,4 +1,4 @@ -// 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.core.report; import java.io.FileOutputStream; @@ -24,7 +24,7 @@ /** * Print all account info. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -34,7 +34,7 @@ public class AccountsReport implements IReport { /** - * + * */ private static final long serialVersionUID = -8628612595677727400L; @@ -43,7 +43,7 @@ public class AccountsReport implements IReport { private IInternalController controller; private Log log; - + private Filter accountFilter = new Filter(); private Filter filter; @@ -51,7 +51,7 @@ public class AccountsReport implements IReport { private void writeSummaryRow(PrintWriter printWriter, int site) { int total = 0; - + ClientType.Type[] types = ClientType.Type.values(); for (ClientType.Type type : types) { Vector vector = contest.getAccounts(type, site); @@ -65,7 +65,7 @@ private void writeSummaryRow(PrintWriter printWriter, int site) { printWriter.format("%7s", total); printWriter.println(); } - + private void printAccountSummaryBySite (PrintWriter printWriter){ Site[] sites = contest.getSites(); @@ -88,6 +88,7 @@ private void printAccountSummaryBySite (PrintWriter printWriter){ } } + @Override public void writeReport(PrintWriter printWriter) { printAccountSummaryBySite (printWriter); printAccountsByGroup (printWriter); @@ -115,30 +116,30 @@ private void printAccountsByGroup(PrintWriter printWriter) { } printWriter.println(); - Account [] sortedAccounts = (Account[]) accounts.toArray(new Account[accounts.size()]); - + Account [] sortedAccounts = accounts.toArray(new Account[accounts.size()]); + Arrays.sort(sortedAccounts, new AccountComparator()); for (Account account : sortedAccounts){ - + printAccount(printWriter, account); } } } } - + private void printAccount(PrintWriter printWriter, Account account) { printWriter.print(" Site " + account.getSiteNumber()); printWriter.format(" %-15s", account.getClientId().getName()); printWriter.println(" id=" + account.getElementId()); - + printWriter.format("%22s"," "); printWriter.print("'"+account.getDisplayName()+"' "); - + if (contest.isAllowed (edu.csus.ecs.pc2.core.security.Permission.Type.VIEW_PASSWORDS)) { printWriter.print("password '" + account.getPassword() + "' "); } - + Permission.Type type = Permission.Type.LOGIN; if (account.isAllowed(type)){ printWriter.print(type+" "); @@ -151,16 +152,27 @@ private void printAccount(PrintWriter printWriter, Account account) { printWriter.format("%22s"," "); printWriter.print("alias '" + account.getAliasName()+"' "); - ElementId groupId = account.getGroupId(); - if (groupId != null) { - Group group = contest.getGroup(groupId); - if (group != null) { - printWriter.print("group '"+group+"' ("+groupId+")"); - } else { - printWriter.print("group invalid ("+groupId+")"); + if(account.getGroupIds() != null) { + printWriter.print("groups '"); + boolean first = true; + for(ElementId elementId: account.getGroupIds()) { + if(first == false) { + printWriter.print(","); + } else { + first = false; + } + Group group = contest.getGroup(elementId); + // I'm not sure how useful it is to show the ElementId here - the CMS Group ID in parens may be more useful. + // But, this is what the code always did, apparently -- JB + if (group != null) { + printWriter.print(group+" ("+elementId+")"); + } else { + printWriter.print("invalid ("+elementId+")"); + } } + printWriter.print("'"); } else { - printWriter.print("group ''"); + printWriter.print("groups ''"); } printWriter.println(); if (account.getClientId().getClientType().equals(ClientType.Type.TEAM)) { @@ -172,16 +184,16 @@ private void printAccount(PrintWriter printWriter, Account account) { printWriter.println(); } String pad7 = " "; - + printWriter.println(pad7 + "Country code '" + account.getCountryCode() + "'"); printWriter.print(pad7 + "Institution name '" + account.getInstitutionName() + "'"); printWriter.print(" Institution code '" + account.getInstitutionCode() + "'"); printWriter.println(" Institution short name '" + account.getInstitutionShortName() + "'"); printWriter.print(pad7 + "Member Names: "); - + String [] names = account.getMemberNames(); - + if (names.length == 0) { printWriter.print(" NO member names assigned "); } else { @@ -201,6 +213,7 @@ private String join(String delimit, String[] names) { return buffer.toString(); } + @Override public void printHeader(PrintWriter printWriter) { printWriter.println(new VersionInfo().getSystemName()); printWriter.println("Date: " + Utilities.getL10nDateTime()); @@ -209,11 +222,13 @@ public void printHeader(PrintWriter printWriter) { printWriter.println(getReportTitle() + " Report"); } + @Override public void printFooter(PrintWriter printWriter) { printWriter.println(); printWriter.println("end report"); } + @Override public void createReportFile(String filename, Filter inFilter) throws IOException { PrintWriter printWriter = new PrintWriter(new FileOutputStream(filename, false), true); @@ -240,32 +255,39 @@ public void createReportFile(String filename, Filter inFilter) throws IOExceptio } } + @Override public String[] createReport(Filter inFilter) { throw new SecurityException("Not implemented"); } + @Override public String createReportXML(Filter inFilter) throws IOException { return Reports.notImplementedXML(this); } + @Override public String getReportTitle() { return "Accounts"; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { this.contest = inContest; this.controller = inController; log = controller.getLog(); } + @Override public String getPluginTitle() { return "Accounts Report"; } + @Override public Filter getFilter() { return filter; } + @Override public void setFilter(Filter filter) { this.filter = filter; } diff --git a/src/edu/csus/ecs/pc2/core/report/EventFeedJSONReport.java b/src/edu/csus/ecs/pc2/core/report/EventFeedJSONReport.java index 96506a652..ea7205ba0 100644 --- a/src/edu/csus/ecs/pc2/core/report/EventFeedJSONReport.java +++ b/src/edu/csus/ecs/pc2/core/report/EventFeedJSONReport.java @@ -1,4 +1,4 @@ -// 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.core.report; import java.io.FileOutputStream; @@ -17,13 +17,13 @@ /** * Event Feed JSON report. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class EventFeedJSONReport implements IReport { /** - * + * */ private static final long serialVersionUID = -3444824680719793748L; @@ -34,7 +34,7 @@ public class EventFeedJSONReport implements IReport { private Log log; private Filter filter = new Filter(); - + private void writeContestTime(PrintWriter printWriter) { printWriter.println(); GregorianCalendar resumeTime = contest.getContestTime().getResumeTime(); @@ -46,31 +46,38 @@ private void writeContestTime(PrintWriter printWriter) { } } + @Override public void writeReport(PrintWriter printWriter) throws IllegalContestState { - + EventFeedJSON efEventFeedJSON = new EventFeedJSON(contest); - + + if(filter != null) { + efEventFeedJSON.setFilter(filter); + } String s = efEventFeedJSON.createJSON(contest, null, null); - + printWriter.print(s); } + @Override public void printHeader(PrintWriter printWriter) { printWriter.println(new VersionInfo().getSystemName()); printWriter.println("Date: " + Utilities.getL10nDateTime()); printWriter.println(new VersionInfo().getSystemVersionInfo()); printWriter.println(); printWriter.println(getReportTitle() + " Report"); - + writeContestTime(printWriter); printWriter.println(); } + @Override public void printFooter(PrintWriter printWriter) { printWriter.println(); printWriter.println("end report"); } + @Override public void createReportFile(String filename, Filter inFilter) throws IOException { PrintWriter printWriter = new PrintWriter(new FileOutputStream(filename, false), true); @@ -79,11 +86,11 @@ public void createReportFile(String filename, Filter inFilter) throws IOExceptio try { printHeader(printWriter); - + writeReport(printWriter); - + printFooter(printWriter); - + } catch (Exception e) { printWriter.println("Exception in report: " + e.getMessage()); e.printStackTrace(printWriter); @@ -99,32 +106,39 @@ public void createReportFile(String filename, Filter inFilter) throws IOExceptio } } + @Override public String[] createReport(Filter inFilter) { throw new SecurityException("Not implemented"); } + @Override public String createReportXML(Filter inFilter) throws IOException { return Reports.notImplementedXML(this); } + @Override public String getReportTitle() { return "Event Feed JSON"; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { this.contest = inContest; this.controller = inController; log = controller.getLog(); } + @Override public String getPluginTitle() { return getReportTitle() + " Report"; } + @Override public Filter getFilter() { return filter; } + @Override public void setFilter(Filter filter) { this.filter = filter; } diff --git a/src/edu/csus/ecs/pc2/core/report/ProblemGroupAssignmentReport.java b/src/edu/csus/ecs/pc2/core/report/ProblemGroupAssignmentReport.java index 3cd3dd38a..e6ad26228 100644 --- a/src/edu/csus/ecs/pc2/core/report/ProblemGroupAssignmentReport.java +++ b/src/edu/csus/ecs/pc2/core/report/ProblemGroupAssignmentReport.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2021 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.core.report; import java.io.FileOutputStream; @@ -24,7 +24,7 @@ /** * Problem Group Assignments. - * + * * @author Douglas A. Lane */ public class ProblemGroupAssignmentReport implements IReport { @@ -32,7 +32,7 @@ public class ProblemGroupAssignmentReport implements IReport { private static final String PAD = " "; /** - * + * */ private static final long serialVersionUID = -1889505494967631248L; @@ -80,10 +80,10 @@ public void createReportFile(String filename, Filter filter) throws IOException printWriter.println("Exception generating report " + e.getMessage()); } } - + public static Account[] getAccounts(IInternalContest contest, ClientType.Type type) { Vector accountVector = contest.getAccounts(type); - Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + Account[] accounts = accountVector.toArray(new Account[accountVector.size()]); return accounts; } @@ -98,7 +98,7 @@ private void writeGroupSummary(PrintWriter printWriter) { // print groups by group Id Arrays.sort(groups, new GroupComparatorById()); - + int longestGroupName = 0; for (Group group : groups) { if (group.getDisplayName().length() > longestGroupName) { @@ -117,21 +117,21 @@ private void writeGroupSummary(PrintWriter printWriter) { printWriter.println(); printWriter.println("--- " + teamAccounts.length + " teams ---"); printWriter.println(); - - + + // for (Account account : teamAccounts) { // printWriter.println("Account "+account +" " +account.getGroupId() + " "+contest.getGroup(account.getGroupId()).getDisplayName()); // } -// +// for (Group group : groups) { int teamsInGroupCount = 0; for (Account account : teamAccounts) { - if (account.getGroupId() != null && group.getElementId().equals(account.getGroupId())){ + if (account.isGroupMember(group.getElementId()) == true){ teamsInGroupCount++; } // printWriter.println("Group "+group.getGroupId()+" vs " +account.getGroupId() + " aka "+contest.getGroup(account.getGroupId()).getDisplayName()); - + } String paddedName = String.format("%-" + longestGroupName + "s", group.getDisplayName()); @@ -226,6 +226,7 @@ public void printHeader(PrintWriter printWriter) { writeContestTime(printWriter); } + @Override public void printFooter(PrintWriter printWriter) { printWriter.println(); printWriter.println("end report"); diff --git a/src/edu/csus/ecs/pc2/core/report/ProblemsGroupReport.java b/src/edu/csus/ecs/pc2/core/report/ProblemsGroupReport.java index 54dab5819..f3f434180 100644 --- a/src/edu/csus/ecs/pc2/core/report/ProblemsGroupReport.java +++ b/src/edu/csus/ecs/pc2/core/report/ProblemsGroupReport.java @@ -1,4 +1,4 @@ -// 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.core.report; import java.io.FileOutputStream; @@ -14,6 +14,7 @@ import edu.csus.ecs.pc2.core.log.Log; import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientType.Type; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Filter; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; @@ -21,13 +22,13 @@ /** * Print summary of groups which can view/use problems. - * + * * @author pc2@ecs.csus.edu */ public class ProblemsGroupReport implements IReport { /** - * + * */ private static final long serialVersionUID = 5635989177738081431L; @@ -84,6 +85,7 @@ private String getYesNo(boolean b) { // Group 2 // Group 3 + @Override public void writeReport(PrintWriter printWriter) { Problem[] problems = contest.getProblems(); @@ -108,7 +110,7 @@ public void writeReport(PrintWriter printWriter) { Group[] groups = contest.getGroups(); for (Group group : groups) { - + String title = StringUtilities.trunc(group.getDisplayName(), maxGroupColumnLength); title = StringUtilities.rpad(' ', 6, group.getGroupId()) + " " + StringUtilities.rpad(' ', maxGroupColumnLength + 1, title); @@ -144,10 +146,10 @@ public void writeReport(PrintWriter printWriter) { } } - + printWriter.println(); } - + printWriter.println(); @@ -204,12 +206,12 @@ private void printGroupsAndViewableProblems(PrintWriter printWriter, Group[] gro private int teamCountPerGroup(Group group) { int teamCount = 0; + ElementId groupElementId = group.getElementId(); + Account[] teams = getTeamAccounts(); for (Account account : teams) { - if (account.getGroupId() != null) { - if (group.getElementId().equals(account.getGroupId())) { - teamCount++; - } + if (account.isGroupMember(groupElementId)) { + teamCount++; } } @@ -221,7 +223,7 @@ private Account[] getTeamAccounts() { if (accounts == null) { Type type = Type.TEAM; Vector accountVector = contest.getAccounts(type); - accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + accounts = accountVector.toArray(new Account[accountVector.size()]); } return accounts; @@ -243,6 +245,7 @@ private String centerString(String string, int length) { } + @Override public void printHeader(PrintWriter printWriter) { printWriter.println(new VersionInfo().getSystemName()); printWriter.println("Date: " + Utilities.getL10nDateTime()); @@ -253,11 +256,13 @@ public void printHeader(PrintWriter printWriter) { writeContestTime(printWriter); } + @Override public void printFooter(PrintWriter printWriter) { printWriter.println(); printWriter.println("end report"); } + @Override public void createReportFile(String filename, Filter inFilter) throws IOException { PrintWriter printWriter = new PrintWriter(new FileOutputStream(filename, false), true); @@ -285,32 +290,39 @@ public void createReportFile(String filename, Filter inFilter) throws IOExceptio } } + @Override public String[] createReport(Filter inFilter) { throw new SecurityException("Not implemented"); } + @Override public String createReportXML(Filter inFilter) throws IOException { return Reports.notImplementedXML(this); } + @Override public String getReportTitle() { return "Groups for Problems"; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { this.contest = inContest; this.controller = inController; log = controller.getLog(); } + @Override public String getPluginTitle() { return "Groups for Problems Report"; } + @Override public Filter getFilter() { return filter; } + @Override public void setFilter(Filter filter) { this.filter = filter; } diff --git a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java index 816f6be62..7327f0598 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 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.core.scoring; import java.io.IOException; @@ -9,6 +9,7 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; +import java.util.List; import java.util.Properties; import java.util.Set; import java.util.TreeMap; @@ -39,12 +40,12 @@ import edu.csus.ecs.pc2.core.model.Judgement; import edu.csus.ecs.pc2.core.model.Problem; import edu.csus.ecs.pc2.core.model.Run; +import edu.csus.ecs.pc2.core.model.Run.RunStates; import edu.csus.ecs.pc2.core.model.RunUtilities; import edu.csus.ecs.pc2.core.model.Site; -import edu.csus.ecs.pc2.core.model.Run.RunStates; import edu.csus.ecs.pc2.core.security.Permission; -import edu.csus.ecs.pc2.core.security.PermissionList; import edu.csus.ecs.pc2.core.security.Permission.Type; +import edu.csus.ecs.pc2.core.security.PermissionList; import edu.csus.ecs.pc2.core.standings.ScoreboardUtilities; import edu.csus.ecs.pc2.core.util.IMemento; import edu.csus.ecs.pc2.core.util.XMLMemento; @@ -52,11 +53,11 @@ /** * Default Scoring Algorithm, implementation of the IScoringAlgorithm. - * + * * This class implements the standard (default) scoring algorithm, which ranks all teams according to number of problems solved, then according to "penalty points" computed by multiplying the number * of "NO" runs on solved problems by the PenaltyPoints value specified in the contest configuration, then finally according to earliest time of last solution (with ties at that level broken * alphabetically). This is the "standard" algorithm used in many ICPC Regional Contests. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -68,11 +69,11 @@ public class DefaultScoringAlgorithm implements IScoringAlgorithm { public static final String POINTS_PER_YES_MINUTE = "Points per Minute (for 1st yes)"; public static final String BASE_POINTS_PER_YES = "Base Points per Yes"; - + public static final String POINTS_PER_NO_COMPILATION_ERROR = "Points per Compilation Error"; - + public static final String POINTS_PER_NO_SECURITY_VIOLATION = "Points per Security Violation"; - + /** * Non-frozen scoreboard output directory key */ @@ -82,20 +83,20 @@ public class DefaultScoringAlgorithm implements IScoringAlgorithm { * Frozen scoreboard output directory key */ public static final String PUBLIC_OUTPUT_DIR = "Output Public HTML dir"; - + /** * properties. - * + * * key=name, value=default_value, type, min, max (colon delimited) */ private static String[][] propList = { { POINTS_PER_NO, "20:Integer" }, { POINTS_PER_YES_MINUTE, "1:Integer" }, { BASE_POINTS_PER_YES, "0:Integer" }, { POINTS_PER_NO_COMPILATION_ERROR, "0:Integer" }, { POINTS_PER_NO_SECURITY_VIOLATION, "0:Integer" }, { JUDGE_OUTPUT_DIR, "html:String" }, { PUBLIC_OUTPUT_DIR, "public_html:String" } }; - + private Properties props = new Properties(); private Object mutex = new Object(); - + private int grandTotalAttempts; private int grandTotalSolutions; @@ -111,23 +112,23 @@ public class DefaultScoringAlgorithm implements IScoringAlgorithm { private int[] problemAttempts = null; private Log log; - + private boolean countPreliminaryJudgements = false; private PermissionList permissionList = new PermissionList(); - + /** * Respect Send to Team Permission. - * + * * true means if {@link edu.csus.ecs.pc2.core.model.JudgementRecord#isSendToTeam()} is true then process run as a NEW run. *
    - * false means process all records per usual. + * false means process all records per usual. */ private boolean respectSendToTeam = false; private boolean respectEOC = false; - + private boolean obeyFreeze = false; - + /** * @return the obeyFreeze @@ -163,14 +164,14 @@ AccountList getAccountList(IInternalContest theContest) { AccountList accountList = new AccountList(); Enumeration accountEnum = accountVect.elements(); while(accountEnum.hasMoreElements()) { - Account a = (Account)accountEnum.nextElement(); + Account a = accountEnum.nextElement(); accountList.add(a); } return accountList; } /** * Get the Score and Statistics information for one problem. - * + * * @return pc2.ex.ProblemScoreData * @param treeMap * java.util.TreeMap @@ -263,11 +264,11 @@ private int getBasePointsPerYes() { private int getPenaltyPointsPerNo() { return (getPropIntValue(POINTS_PER_NO)); } - + private int getPenaltyPointsPerNoCompilationError() { return (getPropIntValue(POINTS_PER_NO_COMPILATION_ERROR)); } - + private int getPenaltyPointsPerNoSecurityViolation() { return (getPropIntValue(POINTS_PER_NO_SECURITY_VIOLATION)); } @@ -286,27 +287,32 @@ public Properties getProperties() { public void setProperties(Properties properties) { this.props = properties; } - + @Override public String getStandings(IInternalContest theContest, Properties properties, Log inputLog) throws IllegalContestState { return getStandings(theContest, null, null, properties, inputLog); } - + /* * (non-Javadoc) - * + * * @see edu.csus.ecs.pc2.core.scoring.ScoringAlgorithm#getStandings(edu.csus.ecs.pc2.core.Run[], edu.csus.ecs.pc2.core.AccountList, edu.csus.ecs.pc2.core.ProblemDisplayList, java.util.Properties) */ @Override public String getStandings(IInternalContest theContest, Run[] runs, Integer divisionNumber, Properties properties, Log inputLog) throws IllegalContestState { + return(getStandings(theContest, runs, divisionNumber, null, properties, inputLog)); + } + + @Override + public String getStandings(IInternalContest theContest, Run[] runs, Integer divisionNumber, List wantedGroups, Properties properties, Log inputLog) throws IllegalContestState { if (theContest == null) { throw new InvalidParameterException("Invalid model (null)"); } - + if (properties == null || properties.isEmpty()) { properties = getProperties(); } - + this.log = inputLog; long freezeSeconds = -1; boolean isThawn = false; @@ -329,30 +335,30 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi log.fine("DEBUG: using freezeSeconds of "+freezeSeconds +" for str "+freezeTime+", with isThawn="+isThawn); } - + // TODO properties should be validated here props = properties; - + /** - * Settings + * Settings */ - - + + respectSendToTeam = isAllowed (theContest, theContest.getClientId(), Permission.Type.RESPECT_NOTIFY_TEAM_SETTING); respectEOC = isAllowed (theContest, theContest.getClientId(), Permission.Type.RESPECT_EOC_SUPPRESSION); countPreliminaryJudgements = theContest.getContestInformation().isPreliminaryJudgementsUsedByBoard(); - + XMLMemento mementoRoot = XMLMemento.createWriteRoot("contestStandings"); IMemento summaryMememento = createSummaryMomento (theContest, mementoRoot); - + AccountList accountList = getAccountList(theContest); Problem[] allProblems = theContest.getProblems(); Hashtable problemsIndexHash = new Hashtable(); int p2 = 0; for (int p=1; p <= allProblems.length ; p++) { Problem prob = allProblems[p-1]; - if (prob.isActive()) { + if (prob.isActive() && prob.canView(wantedGroups)) { p2++; problemsIndexHash.put(prob.getElementId(), new Integer(p2)); } @@ -360,7 +366,7 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi Problem[] problems = new Problem[p2]; Set keys = problemsIndexHash.keySet(); for (Iterator iterator = keys.iterator(); iterator.hasNext();) { - ElementId type = (ElementId) iterator.next(); + ElementId type = iterator.next(); int p = Integer.valueOf(problemsIndexHash.get(type)); problems[p-1] = theContest.getProblem(type); } @@ -370,7 +376,7 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi summaryMememento.putInteger("siteCount", sites.length); Group[] groups = theContest.getGroups(); if (groups != null) { - dumpGroupList(groups, summaryMememento); + dumpGroupList(groups, summaryMememento, wantedGroups); } BalloonSettings[] balloonSettings = theContest.getBalloonSettings(); if (balloonSettings != null) { @@ -384,19 +390,23 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi } } if (runs == null) { - runs = theContest.getRuns(); + // Note: we do not deal with divisionNumber here since + // 1) it is being deprecated + // 2) if a divisionNumber is passed in, the 'runs' will be non-null and pre-filtered for the division. + // here, we only filter the runs based on groups wanted. + runs = ScoreboardUtilities.getGroupFilteredRuns(theContest, wantedGroups); } synchronized (mutex) { Account[] accounts = accountList.getList(); - + /** * This contains the standings records, key is ClientId.toString() value is StandingsRecord */ Hashtable standingsRecordHash = new Hashtable(); - + RunComparatorByTeam runComparatorByTeam = new RunComparatorByTeam(); TreeMap runTreeMap = new TreeMap(runComparatorByTeam); - + Hashtable problemHash = new Hashtable(); for (int i = 0; i < problems.length; i++) { Problem problem = problems[i]; @@ -404,9 +414,9 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi problemHash.put(problem.getElementId().toString(), problem); } } - - initializeStandingsRecordHash (theContest, accountList, accounts, problems, standingsRecordHash, divisionNumber); - + + initializeStandingsRecordHash (theContest, accountList, accounts, problems, standingsRecordHash, divisionNumber, wantedGroups); + for (int i = 0; i < runs.length; i++) { // skip runs that are deleted and // skip runs whose submitter is no longer active and @@ -416,9 +426,9 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi log.info("account could not be located for " + runs[i].getSubmitter()); continue; } - if (!runs[i].isDeleted() && account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD) + if (!runs[i].isDeleted() && account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD) && problemHash.containsKey(runs[i].getProblemId().toString())) { - + Run runToAdd = runs[i]; if ( respectSendToTeam && runToAdd.getAllJudgementRecords().length > 0 ){ /** @@ -447,14 +457,14 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi runToAdd = RunUtilities.createNewRun(runs[i], theContest); } runTreeMap.put(runToAdd, runToAdd); - + } } - + if (!runTreeMap.isEmpty()) { - + generateStandingsValues (runTreeMap, standingsRecordHash, problemsIndexHash, theContest); - + } // else no runs applyScoringAdjustments(standingsRecordHash, accountList); @@ -467,11 +477,11 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi for (StandingsRecord record : enumeration) { treeMap.put(record, record); } - + createStandingXML(treeMap, mementoRoot, accountList, problems, problemsIndexHash, groups, theContest.getContestInformation(), summaryMememento); - + } // mutex - + String xmlString; try { xmlString = mementoRoot.saveToString(); @@ -482,7 +492,7 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi // System.out.println(xmlString); return xmlString; } - + private void applyScoringAdjustments(Hashtable standingsRecordHash, AccountList accountList) { Account[] accounts = accountList.getList(); Hashtable accountHash = new Hashtable(); @@ -495,7 +505,7 @@ private void applyScoringAdjustments(Hashtable standing Set s = standingsRecordHash.keySet(); for (Iterator iterator = s.iterator(); iterator.hasNext();) { - String key = (String) iterator.next(); + String key = iterator.next(); int scoreAdjustment = accountHash.get(key).getScoringAdjustment(); StandingsRecord record = standingsRecordHash.get(key); long penaltyPoints = record.getPenaltyPoints(); @@ -526,7 +536,7 @@ private PermissionList getPermissionList(IInternalContest theContest) { /** * Is Client allowed to do permission type - * + * * @param theContest * @param clientId * @param respect_notify_team_setting @@ -549,7 +559,7 @@ private void dumpBalloonSettings(BalloonSettings balloonSettings, Problem[] prob } } - private void dumpGroupList(Group[] groups, IMemento memento) { + private void dumpGroupList(Group[] groups, IMemento memento, List wantedGroups) { memento.putInteger("groupCount", groups.length+1); IMemento groupsMemento = memento.createChild("groupList"); int id = 0; @@ -565,15 +575,20 @@ private void dumpGroupList(Group[] groups, IMemento memento) { if (groups[i].getSite() != null) { groupMemento.putInteger("pc2Site", groups[i].getSite().getSiteNumber()); } + if(wantedGroups == null || wantedGroups.contains(groups[i])) { + groupMemento.putInteger("included", 1); + } else { + groupMemento.putInteger("included", 0); + } } } /** * Ranks standings records and add standings XML to mementoRoot. - * + * * Loops through the standings records and problem summary information * creating XML blocks: teamStanding and problemSummaryInfo. - * + * * @param treeMap * @param mementoRoot * @param accountList @@ -581,19 +596,15 @@ private void dumpGroupList(Group[] groups, IMemento memento) { * @param problemsIndexHash * @param summaryMememento */ - private void createStandingXML (TreeMap treeMap, XMLMemento mementoRoot, + private void createStandingXML (TreeMap treeMap, XMLMemento mementoRoot, AccountList accountList, Problem[] problems, Hashtable problemsIndexHash, Group[] groups, ContestInformation contestInformation, IMemento summaryMememento) { - + // easy access Hashtable groupHash = new Hashtable(); Hashtable groupIndexHash = new Hashtable(); int groupCount = 0; for (Group group : groups) { - // no reference to groups that should not be displayed on scoreboard - if (!group.isDisplayOnScoreboard()) { - continue; - } groupHash.put(group.getElementId(), group); groupIndexHash.put(group, Integer.valueOf(groupCount)); groupCount++; @@ -625,12 +636,12 @@ private void createStandingXML (TreeMap treeMa // this is the number used in the array constructors, bigger is ok, smaller is not. divisionCount = highestFound; String teamVarDisplayString = contestInformation.getTeamScoreboardDisplayFormat(); - + StandingsRecord[] srArray = new StandingsRecord[treeMap.size()]; - + Collection coll = treeMap.values(); Iterator iterator = coll.iterator(); - + problemBestTime = new int[problems.length + 1]; problemLastTime = new int[problems.length + 1]; problemSolutions = new int[problems.length + 1]; @@ -638,11 +649,11 @@ private void createStandingXML (TreeMap treeMa for (int p = 1; p <= problems.length; p++) { problemBestTime[p] = -1; } - + grandTotalAttempts = 0; grandTotalSolutions = 0; grandTotalProblemAttempts = 0; - + // assign the ranks long numSolved = -1, score = 0, lastSolved = 0; int rank = 0, indexRank = 0; @@ -702,13 +713,18 @@ private void createStandingXML (TreeMap treeMa standingsRecordMemento.putInteger("overallRank", standingsRecord.getRankNumber()); standingsRecordMemento.putInteger("index", index); Account account = accountList.getAccount(standingsRecord.getClientId()); - + + // it is probably OK to use the "primary" group ID here (the one supplied by the CMS). + // this is used to augment the teamName for display {:groupname, :groupid}. Using the CMS group should convey + // the desired information: eg. Hawaii - D2 (for example). Would we want to just show "D2" or "Hawaii" as a default? + // perhaps - but for now we want the compound group name (eg CMS name). + // TODO add a "Settings" option to decide which type of group to use for :groupname? Group group = null; - if (account.getGroupId() != null) { - group = groupHash.get(account.getGroupId()); + if (account.getPrimaryGroupId() != null) { + group = groupHash.get(account.getPrimaryGroupId()); } - - standingsRecordMemento.putString("teamName", ScoreboardVariableReplacer.substituteDisplayNameVariables(teamVarDisplayString, account, group)); + + standingsRecordMemento.putString("teamName", ScoreboardVariableReplacer.substituteDisplayNameVariables(teamVarDisplayString, account, group)); standingsRecordMemento.putInteger("teamId", account.getClientId().getClientNumber()); standingsRecordMemento.putInteger("teamSiteId", account.getClientId().getSiteNumber()); standingsRecordMemento.putString("teamKey", account.getClientId().getTripletKey()); @@ -719,14 +735,14 @@ private void createStandingXML (TreeMap treeMa shortSchoolName = longSchoolName; } if (shortSchoolName != null) { - standingsRecordMemento.putString("shortSchoolName", shortSchoolName); + standingsRecordMemento.putString("shortSchoolName", shortSchoolName); } if (account.getAliasName().trim().equals("")) { standingsRecordMemento.putString("teamAlias", account.getDisplayName()+" (not aliasesd)"); } else { standingsRecordMemento.putString("teamAlias", account.getAliasName().trim()); } - + if (group != null ) { // the group was in groupHash, so must be in groupIndexHash int groupIndex = groupIndexHash.get(group).intValue(); @@ -761,7 +777,7 @@ private void createStandingXML (TreeMap treeMa // current user tied with last user, so same rank standingsRecord.setDivisionRankNumber(divisionRank[divisionIndex]); } - + standingsRecordMemento.putInteger("divisionRank", standingsRecord.getDivisionRankNumber()); } } @@ -795,7 +811,7 @@ private void createStandingXML (TreeMap treeMa problemLastTime[id] = new Long(psi.getSolutionTime()).intValue(); } if (problemBestTime[id] < 0 || psi.getSolutionTime() < problemBestTime[id]) { - problemBestTime[id] = new Long(psi.getSolutionTime()).intValue(); + problemBestTime[id] = new Long(psi.getSolutionTime()).intValue(); } } } @@ -805,15 +821,15 @@ private void createStandingXML (TreeMap treeMa srArray[index++] = standingsRecord; } - + summaryMememento.putInteger("medianProblemsSolved", getMedian(srArray)); generateSummaryTotalsForProblem (problems, problemsIndexHash, summaryMememento); - + } - + /** * Input is a sorted ranking list. What is the median number of problems solved. - * + * * @param srArray * @return median number of problems solved */ @@ -842,7 +858,7 @@ private int getMedian(StandingsRecord[] srArray) { /** * This routine checks and obeys the preliminary judgement rules. - * + * * @param run * @return true if run is judged and the state is valid */ @@ -866,10 +882,10 @@ boolean isValidJudgement(Run run) { } return result; } - + /** * Do these long parameters match the values in the StandingsRecord? - * + * * @param standingsRecord * @param numSolved * @param score @@ -891,16 +907,16 @@ boolean isTeamTied(StandingsRecord standingsRecord, long numSolved, long score, /** * Add Problem Summary totals/info for each problem. - * - * Generate all "problem" blocks in "standingsHeader" block (summaryMemento) - * + * + * Generate all "problem" blocks in "standingsHeader" block (summaryMemento) + * * @param problems * @param problemsIndexHash * @param summaryMememento */ - + private void generateSummaryTotalsForProblem(Problem[] problems, Hashtable problemsIndexHash, IMemento summaryMememento) { - + for (int i = 0; i < problems.length; i++) { int id = i + 1; problemsIndexHash.put(problems[i].getElementId(), new Integer(id)); @@ -921,15 +937,15 @@ private void generateSummaryTotalsForProblem(Problem[] problems, Hashtable runTreeMap, Hashtab if (!lastUser.equals(run.getSubmitter().toString()) || !lastProblem.equals(run.getProblemId().toString())) { if (!problemTreeMap.isEmpty()) { ProblemSummaryInfo problemSummaryInfo = calcProblemScoreData(problemTreeMap, theContest); - StandingsRecord standingsRecord = (StandingsRecord) standingsHash.get(lastUser); + StandingsRecord standingsRecord = standingsHash.get(lastUser); SummaryRow summaryRow = standingsRecord.getSummaryRow(); summaryRow.put(problemsHash.get(problemSummaryInfo.getProblemId()), problemSummaryInfo); standingsRecord.setSummaryRow(summaryRow); @@ -984,7 +1000,7 @@ private void generateStandingsValues(final TreeMap runTreeMap, Hashtab // handle last run if (!problemTreeMap.isEmpty()) { ProblemSummaryInfo problemSummaryInfo = calcProblemScoreData(problemTreeMap, theContest); - StandingsRecord standingsRecord = (StandingsRecord) standingsHash.get(lastUser); + StandingsRecord standingsRecord = standingsHash.get(lastUser); SummaryRow summaryRow = standingsRecord.getSummaryRow(); summaryRow.put(problemsHash.get(problemSummaryInfo.getProblemId()), problemSummaryInfo); standingsRecord.setSummaryRow(summaryRow); @@ -1010,28 +1026,32 @@ private void generateStandingsValues(final TreeMap runTreeMap, Hashtab /** * Initialize the standingsRecordHash. - * + * * @param accountList * @param accounts * @param problems * @param standingsRecordHash - * @param divisionNumber + * @param divisionNumber */ - private void initializeStandingsRecordHash(IInternalContest theContest, AccountList accountList, Account[] accounts, Problem[] problems, Hashtable standingsRecordHash, Integer divisionNumber) { + private void initializeStandingsRecordHash(IInternalContest theContest, AccountList accountList, Account[] accounts, Problem[] problems, Hashtable standingsRecordHash, Integer divisionNumber, List wantedGroups) { for (int i = 0; i < accountList.size(); i++) { Account account = accounts[i]; if (account.getClientId().getClientType() == ClientType.Type.TEAM && account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD)) { - + if (divisionNumber != null) { String div = ScoreboardUtilities.getDivision(theContest, account.getClientId()); - if (!divisionNumber.toString().trim().equals(div.trim())) { + // div may be null if the team is not a member of any division group, but is being shown on the board. + if (div == null || !divisionNumber.toString().trim().equals(div.trim())) { /** * If this account is NOT in the same division as divisionNumber then do not add StandingsRecord, skip to next account. */ continue; } } + if(!ScoreboardUtilities.isWantedTeam(account, wantedGroups)) { + continue; + } StandingsRecord standingsRecord = new StandingsRecord(); SummaryRow summaryRow = standingsRecord.getSummaryRow(); // populate summaryRow with problems @@ -1052,10 +1072,10 @@ private void initializeStandingsRecordHash(IInternalContest theContest, AccountL /** * Create Summary Momento. - * + * * This creates the standingsHeader block. Later other * methods add problem summaries ("problem" blocks) to this block. - * + * * @param mementoRoot */ private IMemento createSummaryMomento(IInternalContest contest, XMLMemento mementoRoot) { @@ -1110,7 +1130,7 @@ private IMemento createSummaryMomento(IInternalContest contest, XMLMemento memen return memento; } - + private String prettyFreezeTime(String freezeTime) { int count = freezeTime.length() - freezeTime.replace(":", "").length(); if (count < 2) { @@ -1144,7 +1164,7 @@ private String prettyFreezeTime(String freezeTime) { } /** - * + * * @return a list of name/value pairs for default scoring properties. */ public static Properties getDefaultProperties() { diff --git a/src/edu/csus/ecs/pc2/core/scoring/IScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/IScoringAlgorithm.java index c90ba54ec..90f89c6e7 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/IScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/IScoringAlgorithm.java @@ -1,23 +1,25 @@ -// Copyright (C) 1989-2023 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.core.scoring; +import java.util.List; import java.util.Properties; import edu.csus.ecs.pc2.core.exception.IllegalContestState; import edu.csus.ecs.pc2.core.log.Log; +import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Run; /** * Interface that every Scoring Algorithm must implement. - * + * * @author pc2@ecs.csus.edu */ public interface IScoringAlgorithm { /** * Returns an XML description of the current contest standings. - * + * * @param theContest * A proxy object referencing the underlying model describing the contest * @param properties @@ -27,10 +29,10 @@ public interface IScoringAlgorithm { * @return An XML descriptor giving standings properties for each team */ String getStandings(IInternalContest theContest, Properties properties, Log log) throws IllegalContestState; - + /** * Returns an XML description of the current contest standings. - * + * * @param theContest * A proxy object referencing the underlying model describing the contest * @param runs null use all runs, otherwise runs to be used. @@ -42,4 +44,21 @@ public interface IScoringAlgorithm { * @throws IllegalContestState */ String getStandings(IInternalContest theContest, Run[] runs, Integer divisionNumber, Properties properties, Log inputLog) throws IllegalContestState; + + /** + * Returns an XML description of the current contest standings filtered by groups (and division). + * + * @param theContest + * A proxy object referencing the underlying model describing the contest + * @param runs null use all runs, otherwise runs to be used. + * @param divisionNumber division number to fetch standings for (null is ok - means all) + * @param wantedGroups List of groups to fetch standings for (null is ok - means all) + * @param properties + * general and implementation specific settings. + * @param inputLog + * a logger, used to add info to the log file and window. + * @return An XML descriptor giving standings properties for each team + * @throws IllegalContestState + */ + String getStandings(IInternalContest theContest, Run[] runs, Integer divisionNumber, List wantedGroups, Properties properties, Log inputLog) throws IllegalContestState; } diff --git a/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java index 1418dae64..873d86a06 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java @@ -1,9 +1,12 @@ -// Copyright (C) 1989-2023 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.core.scoring; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashSet; +import java.util.List; import java.util.Properties; import java.util.Vector; @@ -39,15 +42,15 @@ /** * "New" Scoring Algorithm implementation. - * + * * Uses same SA as the {@link DefaultScoringAlgorithm} - * + * * @author pc2@ecs.csus.edu */ public class NewScoringAlgorithm extends Plugin implements INewScoringAlgorithm { /** - * + * */ private static final long serialVersionUID = -7815725774105747895L; @@ -64,10 +67,10 @@ public class NewScoringAlgorithm extends Plugin implements INewScoringAlgorithm /** * Return a list of regional winners. - * + * *
    * If there is more than one winner (tie) in a region will all winners (rank 1). - * + * * @param contest * @param properties * @return @@ -86,14 +89,14 @@ public StandingsRecord[] getRegionalWinners(IInternalContest contest, Properties } } - return (StandingsRecord[]) outVector.toArray(new StandingsRecord[outVector.size()]); + return outVector.toArray(new StandingsRecord[outVector.size()]); } /** * Get Regional Winner. - * + * * Will return StandingsRecord for a single regional winner. If there is a tie for first place this method will return null. - * + * * @param contest * @param properties * @param group @@ -115,13 +118,7 @@ public StandingsRecord getRegionalWinner(IInternalContest contest, Properties pr // TODO throw exception/indicate an error. continue; } - Group teamGroup = contest.getGroup(account.getGroupId()); - if (teamGroup == null) { - // TODO throw exception/indicate an error. - continue; - } - - if (teamGroup.equals(group)) { + if(account.isGroupMember(group.getElementId())) { if (outRecord != null) { // More than one winner return null; @@ -139,28 +136,46 @@ public StandingsRecord getRegionalWinner(IInternalContest contest, Properties pr public StandingsRecord[] getStandingsRecords(IInternalContest contest, Properties properties) throws IllegalContestState { return getStandingsRecords(contest, null, properties, false, null); } - - private StandingsRecord[] getStandingsRecords(IInternalContest contest, Integer divisionNumber, Properties properties) throws IllegalContestState { - return getStandingsRecords(contest, divisionNumber, properties, false, null); + + private StandingsRecord[] getStandingsRecords(IInternalContest contest, Integer divisionNumber, List wantedGroups, Properties properties) throws IllegalContestState { + return getStandingsRecords(contest, divisionNumber, wantedGroups, properties, false, null); } /** * Returns sorted and ranked StandingsRecord, if honorScoreboadFreeze is true then run results * from the freeze period will be hidden, unless the contest is unfrozen. - * + * * @param contest + * @param divisionNumber for desired standings * @param properties * @param honorScoreboardFreeze - * @param runs + * @param runs * @return ranked StandingsRecords. * @throws IllegalContestState */ public StandingsRecord[] getStandingsRecords(IInternalContest contest, Integer divisionNumber, Properties properties, boolean honorScoreboardFreeze, Run [] runs) throws IllegalContestState { - + return(getStandingsRecords(contest, divisionNumber, null, properties, honorScoreboardFreeze, runs)); + } + + /** + * Returns sorted and ranked StandingsRecord, if honorScoreboadFreeze is true then run results + * from the freeze period will be hidden, unless the contest is unfrozen. + * + * @param contest + * @param divisionNumber for desired standings + * @param wantedGroups List of groups for which standings are to be returned + * @param properties + * @param honorScoreboardFreeze + * @param runs + * @return ranked StandingsRecords. + * @throws IllegalContestState + */ + public StandingsRecord[] getStandingsRecords(IInternalContest contest, Integer divisionNumber, List wantedGroups, Properties properties, boolean honorScoreboardFreeze, Run [] runs) throws IllegalContestState { + if (contest == null){ throw new IllegalArgumentException("contest is null"); } - + setContest(contest); /* @@ -180,10 +195,13 @@ public StandingsRecord[] getStandingsRecords(IInternalContest contest, Integer d continue; } } + if (!ScoreboardUtilities.isWantedTeam(av, wantedGroups)) { + continue; + } accountVector.add(av); } } - Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + Account[] accounts = accountVector.toArray(new Account[accountVector.size()]); // Kludge for DefaultStandingsRecordComparator AccountList accountList = new AccountList(); @@ -193,7 +211,7 @@ public StandingsRecord[] getStandingsRecords(IInternalContest contest, Integer d comparator.setCachedAccountList(accountList); if (runs == null) { - runs = getContest().getRuns(); + runs = ScoreboardUtilities.getGroupFilteredRuns(getContest(), wantedGroups); } respectEOC = isAllowed(getContest(), getContest().getClientId(), Permission.Type.RESPECT_EOC_SUPPRESSION); @@ -239,7 +257,7 @@ private Run[] filterRunsbyEOC(IInternalContest contest, Run[] runs) { vector.add(runToAdd); } - return (Run[]) vector.toArray(new Run[vector.size()]); + return vector.toArray(new Run[vector.size()]); } private Run[] filterRunsByScoreboardFreeze(IInternalContest contest, Run[] runs) { @@ -261,33 +279,47 @@ private Run[] filterRunsByScoreboardFreeze(IInternalContest contest, Run[] runs) vector.add(runToAdd); } - return (Run[]) vector.toArray(new Run[vector.size()]); + return vector.toArray(new Run[vector.size()]); } - + @Override public String getStandings(IInternalContest contest, Properties properties, Log inputLog) throws IllegalContestState { return getStandings(contest, null, null, properties, inputLog); } - + @Override // TODO SA SOMEDAY Move this to a SA Utility Class // returns XML String for standings. public String getStandings(IInternalContest contest, Run[] runs, Integer divisionNumber, Properties properties, Log inputLog) throws IllegalContestState { + return(getStandings(contest, runs, divisionNumber, null, properties, inputLog)); + } + @Override + // TODO SA SOMEDAY Move this to a SA Utility Class + // returns XML String for standings. + public String getStandings(IInternalContest contest, Run[] runs, Integer divisionNumber, List wantedGroups, Properties properties, Log inputLog) throws IllegalContestState { - StandingsRecord[] standings = getStandingsRecords(contest, divisionNumber, properties); + StandingsRecord[] standings = getStandingsRecords(contest, divisionNumber, wantedGroups, properties); XMLMemento mementoRoot = XMLMemento.createWriteRoot("contestStandings"); IMemento summaryMememento = createSummaryMomento(contest.getContestInformation(), mementoRoot); - dumpGroupList(contest.getGroups(), mementoRoot); + dumpGroupList(contest.getGroups(), mementoRoot, wantedGroups); + + ArrayList probArray = new ArrayList(); + + for(Problem prob : contest.getProblems()) { + if (prob.isActive() && prob.canView(wantedGroups)) { + probArray.add(prob); + } + } + Problem[] problems = probArray.toArray(new Problem[0]); - Problem[] problems = contest.getProblems(); summaryMememento.putLong("problemCount", problems.length); Site[] sites = contest.getSites(); summaryMememento.putInteger("siteCount", sites.length); Group[] groups = contest.getGroups(); if (groups != null) { - dumpGroupList(groups, summaryMememento); + dumpGroupList(groups, summaryMememento, wantedGroups); } int indexNumber = 0; @@ -296,7 +328,7 @@ public String getStandings(IInternalContest contest, Run[] runs, Integer divisio indexNumber++; } - GrandTotals grandTotals = addProblemSummaryMememento(summaryMememento, standings, contest, contest.getProblems()); + GrandTotals grandTotals = addProblemSummaryMememento(summaryMememento, standings, contest, problems); addGrandTotals(summaryMememento, grandTotals); @@ -314,7 +346,7 @@ public String getStandings(IInternalContest contest, Run[] runs, Integer divisio } /** - * + * * @param standingsRecord * @param standingsRecord2 * @return true if records are tied. @@ -398,7 +430,7 @@ private void assignGroupRanks(IInternalContest contest, StandingsRecord[] standi int groupRank = 0; int lastRank = 0; - int groupId = group.getGroupId(); + ElementId groupElementId = group.getElementId(); for (StandingsRecord standingsRecord : standings) { @@ -408,21 +440,7 @@ private void assignGroupRanks(IInternalContest contest, StandingsRecord[] standi continue; } - ElementId groupElementId = account.getGroupId(); - if (groupElementId == null) { - // TODO throw exception/indicate an error. - continue; - } - - Group teamGroup = contest.getGroup(groupElementId); - if (teamGroup == null) { - // TODO throw exception/indicate an error. - continue; - } - - int teamGroupId = teamGroup.getGroupId(); - - if (groupId == teamGroupId) { + if(account.isGroupMember(groupElementId)) { if (lastRank != standingsRecord.getRankNumber()) { lastRank = standingsRecord.getRankNumber(); groupRank++; @@ -435,7 +453,7 @@ private void assignGroupRanks(IInternalContest contest, StandingsRecord[] standi /** * Create XML problem (for all problems). - * + * * @param summaryMememento * @param standings * @param contest @@ -499,7 +517,7 @@ private GrandTotals addProblemSummaryMememento(IMemento summaryMememento, Standi /** * Create an XML teamStanding element for a team. - * + * * @param mementoRoot * @param contest * @param standingsRecord @@ -510,13 +528,10 @@ private GrandTotals addProblemSummaryMememento(IMemento summaryMememento, Standi private IMemento addTeamMemento(IMemento mementoRoot, IInternalContest contest, StandingsRecord standingsRecord, int indexNumber) { IMemento standingsRecordMemento = mementoRoot.createChild("teamStanding"); - + String teamVarDisplayString = contest.getContestInformation().getTeamScoreboardDisplayFormat(); - ElementId groupId = contest.getAccount(standingsRecord.getClientId()).getGroupId(); - Group group = null; - if (groupId != null) { - group = contest.getGroup(groupId); - } + Account account = contest.getAccount(standingsRecord.getClientId()); + HashSet groups = account.getGroupIds(); // if (standingsRecord.getNumberSolved() > 0){ standingsRecordMemento.putLong("firstSolved", standingsRecord.getFirstSolved()); @@ -526,10 +541,18 @@ private IMemento addTeamMemento(IMemento mementoRoot, IInternalContest contest, standingsRecordMemento.putInteger("solved", standingsRecord.getNumberSolved()); standingsRecordMemento.putInteger("rank", standingsRecord.getRankNumber()); standingsRecordMemento.putInteger("index", indexNumber); - Account account = contest.getAccount(standingsRecord.getClientId()); - + + // it is probably OK to use the "primary" group ID here (the one supplied by the CMS). + // this is used to augment the teamName for display {:groupname, :groupid}. Using the CMS group should convey + // the desired information: eg. Hawaii - D2 (for example). Would we want to just show "D2" or "Hawaii" as a default? + // perhaps - but for now we want the compound group name (eg CMS name). + // TODO add a "Settings" option to decide which type of group to use for :groupname? + Group group = null; + if (account.getPrimaryGroupId() != null) { + group = contest.getGroup(account.getPrimaryGroupId()); + } standingsRecordMemento.putString("teamName", ScoreboardVariableReplacer.substituteDisplayNameVariables(teamVarDisplayString, account, group)); - + standingsRecordMemento.putInteger("teamId", account.getClientId().getClientNumber()); standingsRecordMemento.putInteger("teamSiteId", account.getClientId().getSiteNumber()); standingsRecordMemento.putString("teamKey", account.getClientId().getTripletKey()); @@ -540,14 +563,21 @@ private IMemento addTeamMemento(IMemento mementoRoot, IInternalContest contest, standingsRecordMemento.putString("teamAlias", account.getAliasName().trim()); } - ElementId elementId = account.getGroupId(); - if (elementId != null && contest.getGroup(elementId) != null) { + if(group != null) { standingsRecordMemento.putInteger("groupRank", standingsRecord.getGroupRankNumber()); - standingsRecordMemento.putString("teamGroupName", group.getDisplayName()); - // TODO dal CRITICAL - // standingsRecordMemento.putInteger("teamGroupId", group.get()+1); - standingsRecordMemento.putInteger("teamGroupExternalId", group.getGroupId()); } +// TODO: should change algorithm to compute the group rank for each group the team is a member of and report +// all of them in the standings xml. right now we only do the "primary group Id" for groupRank. +// This is a relatively involved code change. +// if(groups != null) { +// for(ElementId groupElementId: groups) { +// group = contest.getGroup(groupElementId); +// if(group != null) { +// this.addGroupRow(standingsRecordMemento, standingsRecord.getGroupRankNumber(), group); +// } +// } +// } + Problem[] problems = contest.getProblems(); for (int i = 0; i < problems.length; i++) { @@ -560,7 +590,7 @@ private IMemento addTeamMemento(IMemento mementoRoot, IInternalContest contest, /** * Creates all standings records for all teams, unsorted/unranked. - * + * * @param runs * @param accounts * @param properties @@ -675,7 +705,7 @@ public ProblemScoreRecord createProblemScoreRecord(Run[] runs, Problem problem, int submissionsBeforeYes = 0; int compilationErrorsBeforeYes = 0; int securityViolationBeforeYes = 0; - + int numberPending = 0; int numberJudged = 0; @@ -688,46 +718,46 @@ public ProblemScoreRecord createProblemScoreRecord(Run[] runs, Problem problem, } numberSubmissions++; - + // System.out.println(numberSubmissions + " "+run.getElapsedMins()+ " "+run); - + if (run.isJudged()) { numberJudged++; } else { numberPending++; } - + if (run.isSolved() && solutionTime == 0) { // set to solved, set solution time solved = true; solutionTime = run.getElapsedMins(); solvingRun = run; } - + if (run.isJudged() && (!solved)) { - + // before first yes. - + ElementId elementId = run.getJudgementRecord().getJudgementId(); Judgement judgment = getContest().getJudgement(elementId); - + if (Judgement.ACRONYM_COMPILATION_ERROR.equals(judgment.getAcronym())) { compilationErrorsBeforeYes++; } else if (Judgement.ACRONYM_SECURITY_VIOLATION.equals(judgment.getAcronym())) { securityViolationBeforeYes++; } else { - submissionsBeforeYes++; + submissionsBeforeYes++; } } } if (solved) { - points = (solutionTime * getMinutePenalty(properties) + getYesPenalty(properties)) + // + points = (solutionTime * getMinutePenalty(properties) + getYesPenalty(properties)) + // (submissionsBeforeYes * getNoPenalty(properties)) + // (compilationErrorsBeforeYes * getCEPenalty(properties)) + // (securityViolationBeforeYes * getSVPenalty(properties)); } - + return new ProblemScoreRecord(solved, solvingRun, problem, points, solutionTime, numberSubmissions, submissionsBeforeYes, numberPending, numberJudged); } @@ -742,7 +772,7 @@ private int getSVPenalty(Properties properties) { /** * Add XML problemSummaryInfo. - * + * * @param mementoRoot * @param index * @param summaryInfo @@ -775,9 +805,28 @@ private ProblemSummaryInfo createProblemSummaryInfo(Run[] runs, Problem problem, return summaryInfo; } + /** + * Add XML group info for a team. + * + * @param mementoRoot xml root to add to + * @param index rank + * @param group PC2 Group + * @return the xml node + */ + private IMemento addGroupRow(IMemento mementoRoot, int index, Group group) { + + IMemento summaryInfoMemento = mementoRoot.createChild("groupInfo"); + summaryInfoMemento.putInteger("groupRank", index); + summaryInfoMemento.putString("teamGroupName", group.getDisplayName()); + // TODO dal CRITICAL + // summaryInfoMemento.putInteger("teamGroupId", group.get()+1); + summaryInfoMemento.putInteger("teamGroupExternalId", group.getGroupId()); + return summaryInfoMemento; + } + /** * Get all runs for a team/clientid and problem. - * + * * @param runs * @param clientId * @param problem @@ -793,7 +842,7 @@ public Run[] getRuns(Run[] runs, ClientId clientId, Problem problem) { } } } - return (Run[]) vector.toArray(new Run[vector.size()]); + return vector.toArray(new Run[vector.size()]); } @@ -808,7 +857,7 @@ private StandingsRecord[] generateStandingsRecords(Account[] accounts, Problem[] vector.add(standingsRecord); } - return (StandingsRecord[]) vector.toArray(new StandingsRecord[vector.size()]); + return vector.toArray(new StandingsRecord[vector.size()]); } private SummaryRow getBlankSummaryRow(Problem[] problems) { @@ -830,9 +879,9 @@ private SummaryRow getBlankSummaryRow(Problem[] problems) { /** * use block ranking algorithm. - * + * * See {@link #setBlockRanking(boolean)} for more info. - * + * * @return */ public boolean useBlockRanking() { @@ -841,27 +890,27 @@ public boolean useBlockRanking() { /** * Set block ranking when teams have tied rankings. - * + * * This only applies if there are tied teams. IF there are tied teams there are two ways to rank teams after the tied teams. *

    * For example, blocked ranks are: - * + * *

          * 1.  Team 7
          * 1.  Team 2
          * 3.  Team 4
          * 4.  Team 8
          * 
    - * + * * Non-blocked ranks are: - * + * *
          * 1.  Team 7
          * 1.  Team 2
          * 2.  Team 4
          * 3.  Team 8
          * 
    - * + * * @param blockRanking */ public void setBlockRanking(boolean blockRanking) { @@ -880,7 +929,7 @@ private void initializePermissions(IInternalContest contest, ClientId clientId) /** * Is Client allowed to do permission type - * + * * @param contest * @param clientId * @param type @@ -900,9 +949,9 @@ private void addGrandTotals(IMemento summaryMememento, GrandTotals grandTotals) /** * Create XML standingsHeader. - * + * * This creates the standingsHeader block. Later other methods add problem summaries ("problem" blocks) to this block. - * + * * @param mementoRoot */ // TODO SA SOMEDAY Move this to a SA Utility Class @@ -943,11 +992,11 @@ private IMemento createSummaryMomento(ContestInformation contestInformation, XML /** * Create XML groupList. - * + * * @param groups * @param memento */ - private void dumpGroupList(Group[] groups, IMemento memento) { + private void dumpGroupList(Group[] groups, IMemento memento, List wantedGroups) { memento.putInteger("groupCount", groups.length + 1); IMemento groupsMemento = memento.createChild("groupList"); int id = 0; @@ -963,12 +1012,17 @@ private void dumpGroupList(Group[] groups, IMemento memento) { if (groups[i].getSite() != null) { groupMemento.putInteger("pc2Site", groups[i].getSite().getSiteNumber()); } + if(wantedGroups == null || wantedGroups.contains(groups[i])) { + groupMemento.putInteger("included", 1); + } else { + groupMemento.putInteger("included", 0); + } } } /** * Grand totals per team. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ diff --git a/src/edu/csus/ecs/pc2/core/standings/ScoreboardUtilities.java b/src/edu/csus/ecs/pc2/core/standings/ScoreboardUtilities.java index a2b95136a..9309e2640 100644 --- a/src/edu/csus/ecs/pc2/core/standings/ScoreboardUtilities.java +++ b/src/edu/csus/ecs/pc2/core/standings/ScoreboardUtilities.java @@ -1,10 +1,12 @@ -// Copyright (C) 1989-2023 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.core.standings; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Properties; @@ -30,6 +32,7 @@ import edu.csus.ecs.pc2.core.Utilities; import edu.csus.ecs.pc2.core.exception.IllegalContestState; import edu.csus.ecs.pc2.core.log.StaticLog; +import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; import edu.csus.ecs.pc2.core.model.ElementId; @@ -44,13 +47,13 @@ public class ScoreboardUtilities { /** * Create x from XML StringContestStandings - * + * * @param xmlString * @return * @throws JAXBException - * @throws IOException - * @throws JsonMappingException - * @throws JsonParseException + * @throws IOException + * @throws JsonMappingException + * @throws JsonParseException */ public static ContestStandings createContestStandings(String xmlString) throws JAXBException, JsonParseException, JsonMappingException, IOException { @@ -58,7 +61,7 @@ public static ContestStandings createContestStandings(String xmlString) throws J // JAXBContext jaxbContext = JAXBContext.newInstance(ContestStandings.class); // Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); // ContestStandings contestStandings = (ContestStandings) jaxbUnmarshaller.unmarshal(new InputSource(new StringReader(xmlString))); - + XmlMapper xmlMapper = new XmlMapper(); ContestStandings standings = xmlMapper.readValue(xmlString, ContestStandings.class); return standings; @@ -66,7 +69,7 @@ public static ContestStandings createContestStandings(String xmlString) throws J /** * Create ContestStandings from file - * + * * @param xmlFile * @return * @throws JAXBException @@ -86,19 +89,19 @@ public static String createScoreboardXML(IInternalContest contest) throws Illega String xml = scoringAlgorithm.getStandings(contest, properties, StaticLog.getLog()); return xml; } - + public static ContestStandings createContestStandings(IInternalContest contest) throws JAXBException, IllegalContestState, JsonParseException, JsonMappingException, IOException { String xmlString = ScoreboardUtilities.createScoreboardXML(contest); return createContestStandings(xmlString); } - - + + public static List createStandingsRecords (String jsonString, String source) throws JsonProcessingException, IOException{ List list = new ArrayList(); - + ObjectMapper mapper = JSONObjectMapper.getObjectMapper(); JsonNode tree = mapper.readTree(jsonString); - + for (JsonNode jsonNode : tree) { if (jsonNode.isArray()) { @@ -121,20 +124,20 @@ public static List createStandingsRecords (String jsonString, record.setSolved(scoreNode.get("num_solved").asInt()); record.setPoints(scoreNode.get("total_time").asInt()); - + if (record.getTeamId() != 0) { list.add(record); } } } } - + return list; } /** * Get the scoring properties from the model. - * + * * @return scoring properties */ public static Properties getScoringProperties(IInternalContest contest) { @@ -149,7 +152,7 @@ public static Properties getScoringProperties(IInternalContest contest) { /** * Fill in with default properties if not using them. */ - String[] keys = (String[]) defProperties.keySet().toArray(new String[defProperties.keySet().size()]); + String[] keys = defProperties.keySet().toArray(new String[defProperties.keySet().size()]); for (String key : keys) { if (!properties.containsKey(key)) { properties.put(key, defProperties.get(key)); @@ -170,9 +173,9 @@ public Document createDocument(String xml) throws ParserConfigurationException, } public static String loadFileContents(File file) throws IOException { - + String[] lines = Utilities.loadFile(file.getAbsolutePath()); - + String contents = lines[0]; if (lines.length > 1) { contents = String.join("", lines); @@ -194,7 +197,7 @@ public static int toInt(String string, int defaultNumber) { public static Run[] getRunsForUserDivision(ClientId clientId, IInternalContest contest) { String division = getDivision(contest, clientId); - + // System.out.println("debug 22 getRunsForUserDivision for "+clientId+" div is "+division); if (ClientType.Type.TEAM.equals(clientId.getClientType())) { @@ -216,13 +219,13 @@ public static Run[] getRunsForUserDivision(ClientId clientId, IInternalContest c } } - return (Run[]) theDivisionTeamRuns.toArray(new Run[theDivisionTeamRuns.size()]); - + return theDivisionTeamRuns.toArray(new Run[theDivisionTeamRuns.size()]); + } else { return contest.getRuns(); } } - + public static Run[] getRunsForDivision(IInternalContest contest, String division) { List theDivisionTeamRuns = new ArrayList(); @@ -234,7 +237,7 @@ public static Run[] getRunsForDivision(IInternalContest contest, String division } } - return (Run[]) theDivisionTeamRuns.toArray(new Run[theDivisionTeamRuns.size()]); + return theDivisionTeamRuns.toArray(new Run[theDivisionTeamRuns.size()]); } @@ -261,34 +264,40 @@ protected static boolean matchDivsion(IInternalContest contest, String inputDivi /** * Return division for input clientId. - * + * + * TODO To be deprecated when multiple groups are fully implemented. Although, we have to see if anyone still + * uses this. + * * @param contest * @param submitter * @return null if no division, else a digit */ public static String getDivision(IInternalContest contest, ClientId submitter) { - - ElementId groupId = contest.getAccount(submitter).getGroupId(); - if (groupId == null) { - return null; - } - - Group group = contest.getGroup(groupId); - if (group == null) { - return null; + + HashSet groups = contest.getAccount(submitter).getGroupIds(); + String groupName = null; + + if(groups != null) { + for(ElementId elementId: groups) { + Group group = contest.getGroup(elementId); + if(group != null) { + groupName = getDivision(group.getDisplayName()); + if(groupName != null) { + break; + } + } + } } - - String groupName = group.getDisplayName().trim(); - - return getDivision(groupName); + + return groupName; } /** * Return division number from groupName * @param groupName - * @return null if no division nubmer found, else the division number + * @return null if no division number found, else the division number */ - // TODO REFACTOR i689 redesign how divisions are identified. + // TODO REFACTOR i689 redesign how divisions are identified. public static String getDivision(String groupName) { int idx = groupName.lastIndexOf('D'); @@ -301,4 +310,73 @@ public static String getDivision(String groupName) { return null; } + /** + * Get the runs that are only for the desired groups. + * If null, then all runs are returned. + * + * @param theContest (used for getting accounts and runs for the contest) + * @param wantedGroups + * @return array of Run filtered by division and groups + */ + public static Run [] getGroupFilteredRuns(IInternalContest theContest, List wantedGroups) { + + Run [] runs = theContest.getRuns(); + if(wantedGroups != null && wantedGroups.size() > 0) { + + // hash map to speed up looking up Account from client id. theContest.getAccount() is grossly inefficient + HashMap clientToAccount = new HashMap(); + ArrayList newruns = new ArrayList(); + ClientId runClient; + String cKey; + Account runAccount; + + // build a new ArrayList of runs that satisify the wanted group filter + for(Run r : runs) { + runClient = r.getSubmitter(); + cKey = runClient.getTripletKey(); + runAccount = clientToAccount.get(cKey); + if(runAccount == null) { + // not in hash table, so we must look it up, then add it to hash table + runAccount = theContest.getAccount(runClient); + if(runAccount == null) { + // sanity check - there better be an account for the run, or we'll just ignore the run. + continue; + } + clientToAccount.put(cKey, runAccount); + } + for(Group group : wantedGroups) { + if(runAccount.isGroupMember(group.getElementId())) { + newruns.add(r); + } + } + } + // convert to Run [] */ + runs = newruns.toArray(new Run [0]); + } + return(runs); + } + + /** + * Checks if the supplied account is a member of any of the groups in the supplied List of groups + * + * @param account to check + * @param wantedGroups + * @return true if the account is in one of the wanted groups, false otherwise + */ + public static boolean isWantedTeam(Account account, List wantedGroups) { + // Assume we want this account + boolean ret = true; + if(wantedGroups != null && wantedGroups.size() > 0) { + boolean found = false; + // restricted to these groups only + for(Group group : wantedGroups) { + if(account.isGroupMember(group.getElementId())) { + found = true; + break; + } + } + ret = found; + } + return(ret); + } } diff --git a/src/edu/csus/ecs/pc2/core/util/JSONTool.java b/src/edu/csus/ecs/pc2/core/util/JSONTool.java index 9a8cb7c9f..b5889e11d 100644 --- a/src/edu/csus/ecs/pc2/core/util/JSONTool.java +++ b/src/edu/csus/ecs/pc2/core/util/JSONTool.java @@ -1,8 +1,9 @@ -// Copyright (C) 1989-2022 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.core.util; import java.util.Calendar; import java.util.Date; +import java.util.HashSet; import java.util.Properties; import java.util.TimeZone; import java.util.logging.Level; @@ -37,11 +38,11 @@ /** * JSON for pc2 classes. - * + * * @author Troy Boudreau */ public class JSONTool { - + /** * A default localhost location. */ @@ -64,7 +65,7 @@ public JSONTool(IInternalContest model, IInternalController controller) { /** * Create JSON for submissions. - * + * * @param submission */ public ObjectNode convertToJSON(Run submission, HttpServletRequest servletRequest, SecurityContext sc) { @@ -78,19 +79,19 @@ public ObjectNode convertToJSON(Run submission, HttpServletRequest servletReques if (submission.getEntryPoint() != null) { element.put("entry_point", new String(submission.getEntryPoint())); } - - - + + + // FIXME we need separate event feeds for public and admin/analyst // FIXME perhaps change sc to a boolean for public or not? // if (servletRequest != null && (sc != null && sc.isUserInRole("admin") || sc.isUserInRole("analyst"))) { - - + + // TODO shadow add time and mime elements to submission // element.put("mime","application/zip"); - + String pathValue = "/contest/submissions/" + submission.getNumber() + "/files"; - + ObjectMapper mymapper = new ObjectMapper(); ArrayNode arrayNode = mymapper.createArrayNode(); ObjectNode objectNode = mymapper.createObjectNode(); @@ -106,22 +107,22 @@ public ObjectNode convertToJSON(Run submission, HttpServletRequest servletReques * @return empty string if settings is null or empty string, otherwise API base url */ private String getAPIURL() { - + String url = ""; ContestInformation contestInformation = model.getContestInformation(); String primaryCCS_URL = contestInformation.getPrimaryCCS_URL(); if (! StringUtilities.isEmpty(primaryCCS_URL)){ url = primaryCCS_URL.trim(); } - + return url; } private void logWarn(String string, Exception e) { - + System.err.println(string); e.printStackTrace(System.err); - + Log log = controller.getLog(); log.log(Level.WARNING, string, e); } @@ -200,7 +201,7 @@ public ObjectNode convertToJSON(Clarification clarification, ClarificationAnswer /** * This converts ContestInformation to a /state object - * + * * @param ci * @return */ @@ -248,7 +249,7 @@ public ObjectNode toStateJSON(ContestInformation ci) { /** * This converts ContestInformation to a /contest object - * + * * @param ci * @return */ @@ -337,7 +338,7 @@ public ObjectNode convertToJSON(Judgement judgement) { /** * returns true if the value is not null and is not the empty string - * + * * @param value * @return */ @@ -386,10 +387,12 @@ public ObjectNode convertToJSON(Account account) { if (notEmpty(account.getInstitutionCode()) && !account.getInstitutionCode().equals("undefined")) { element.put("organization_id", getOrganizationId(account)); } - if (account.getGroupId() != null) { + HashSet groups = account.getGroupIds(); + if (groups != null) { ArrayNode groupIds = mapper.createArrayNode(); - // FIXME eventually accounts should have more then 1 groupId, make sure add them - groupIds.add(getGroupId(model.getGroup(account.getGroupId()))); + for(ElementId elementId : groups) { + groupIds.add(getGroupId(model.getGroup(elementId))); + } element.set("group_ids", groupIds); } return element; @@ -459,7 +462,7 @@ private Calendar calculateElapsedWalltime(IInternalContest contest, long elapsed /** * Create JSON for judgement. - * + * * @param submission */ public ObjectNode convertJudgementToJSON(Run submission) { @@ -474,14 +477,14 @@ public ObjectNode convertJudgementToJSON(Run submission) { if (submission.isJudged()) { JudgementRecord judgementRecord = submission.getJudgementRecord(); - + // only output its judgement and end times if this is the final judgement if (!judgementRecord.isPreliminaryJudgement()) { // Fetch judgement_type_id from judgement acronym String judgmentAcronym = getJudgementAcronymn(judgementRecord); element.put("judgement_type_id", judgmentAcronym); - + Calendar wallElapsed = calculateElapsedWalltime(model, judgementRecord.getWhenJudgedTime() * 60000); if (wallElapsed != null) { element.put("end_time", Utilities.getIso8601formatter().format(wallElapsed.getTime())); @@ -490,18 +493,18 @@ public ObjectNode convertJudgementToJSON(Run submission) { element.put("end_contest_time", ContestTime.formatTimeMS(judgementRecord.getWhenJudgedTime() * 60000)); } } - + return element; } /** * Fetch Judgement Acronym for run judgement. - * + * * @param judgementRecord * @return judgement acronym. */ private String getJudgementAcronymn(JudgementRecord judgementRecord) { - + ElementId judgementId = judgementRecord.getJudgementId(); Judgement judgement = model.getJudgement(judgementId); return judgement.getAcronym(); diff --git a/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML.java b/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML.java index 67bb3632d..7362992ac 100644 --- a/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML.java +++ b/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML.java @@ -1,4 +1,4 @@ -// 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.exports.ccs; import java.io.File; @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Hashtable; import java.util.Vector; @@ -19,6 +20,7 @@ import edu.csus.ecs.pc2.core.list.RunComparator; import edu.csus.ecs.pc2.core.list.RunTestCaseComparator; import edu.csus.ecs.pc2.core.log.Log; +import edu.csus.ecs.pc2.core.log.StaticLog; import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.BalloonDeliveryInfo; import edu.csus.ecs.pc2.core.model.BalloonSettings; @@ -50,19 +52,19 @@ /** * Event Feed (CCS) XML. - * - * Class used + * + * Class used *
      *
    • to CCS Standard Event Feed output XML based on contest data. *
    - * + * * Mementos are the internal tags for a element, Elements are the XML Elements with * surrounding tag. *

    * For example {@link #createElement(IInternalContest, Language, int)} will create * an element with a {@link #LANGUAGE_TAG} whereas the contents of the memento * {@link #addMemento(IMemento, IInternalContest, Language, int)}. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -103,15 +105,15 @@ public class EventFeedXML { public static final String NOTIFICATION_TAG = "notification"; private RunComparator runComparator = new RunComparator(); - + private BalloonSettings colorSettings = null; private Log log = null; - + private static final String DEFAULT_ACRONYM = "??"; private static final String DEFAULT_COLORS_FILENAME = "colors.txt";; - + private String[] acronymList = { // "No - Compilation Error;CE", // "No - Security Violation;SV", // @@ -147,7 +149,7 @@ private String guessAcronym(String judgementText) { public String toXML(IInternalContest contest) { return toXML(contest, new Filter()); } - + /** * Return freeze XML. * @param contest @@ -155,13 +157,13 @@ public String toXML(IInternalContest contest) { * @return */ public String toXMLFreeze(IInternalContest contest, long minutesFromEnd) { - + long mins = contest.getContestTime().getContestLengthMins() - minutesFromEnd; - + Filter filter = new Filter(); // only get XML elements for events before mins filter.setEndElapsedTime(mins); - + return toXML(contest, filter); } @@ -184,7 +186,7 @@ public String toXML(IInternalContest contest, Filter filter) { for (Judgement judgement : judgements) { memento = mementoRoot.createChild(JUDGEMENT_TAG); addMemento(memento, contest, judgement); - } + } Language[] languages = contest.getLanguages(); int num = 1; @@ -208,7 +210,7 @@ public String toXML(IInternalContest contest, Filter filter) { Vector teams = contest.getAccounts(Type.TEAM); - Account[] accounts = (Account[]) teams.toArray(new Account[teams.size()]); + Account[] accounts = teams.toArray(new Account[teams.size()]); Arrays.sort(accounts, new AccountComparator()); for (Account account : accounts) { @@ -270,7 +272,7 @@ public String toXML(IInternalContest contest, Filter filter) { addMemento( memento, contest, run, notificationSequenceNumber); notificationSequenceNumber ++; } - + FinalizeData finalizeData = contest.getFinalizeData(); if (finalizeData != null) { if (finalizeData.isCertified()) { @@ -284,7 +286,7 @@ public String toXML(IInternalContest contest, Filter filter) { /** * return first run for client and problem. - * + * * @param contest * @param clientId * @param problemId @@ -313,7 +315,7 @@ public Run getFirstSolvedRun(IInternalContest contest, ClientId clientId, Elemen public BalloonDeliveryInfo[] getBalloonDeliveries(IInternalContest contest) { // TODO CCS code frozen restriction - + BalloonDeliveryInfo[] deliveries = new BalloonDeliveryInfo[0]; BalloonSettings balloonSettings = contest.getBalloonSettings(contest.getSiteNumber()); @@ -326,7 +328,7 @@ public BalloonDeliveryInfo[] getBalloonDeliveries(IInternalContest contest) { Hashtable deliveryHash = settings.getBalloonList(); ArrayList balloonDeliveryArray = Collections.list(deliveryHash.elements()); - BalloonDeliveryInfo[] balloonDeliveryInfos = (BalloonDeliveryInfo[]) balloonDeliveryArray.toArray(new BalloonDeliveryInfo[balloonDeliveryArray.size()]); + BalloonDeliveryInfo[] balloonDeliveryInfos = balloonDeliveryArray.toArray(new BalloonDeliveryInfo[balloonDeliveryArray.size()]); return balloonDeliveryInfos; } @@ -334,7 +336,7 @@ public BalloonDeliveryInfo[] getBalloonDeliveries(IInternalContest contest) { /** * create info XML element - * + * * @param contest * @param filter * @return @@ -373,7 +375,7 @@ public IMemento addInfoMemento(IMemento memento, IInternalContest contest, Conte } } - XMLUtilities.addChild(memento, "length", contestLengthString); + XMLUtilities.addChild(memento, "length", contestLengthString); XMLUtilities.addChild(memento, "penalty", DefaultScoringAlgorithm.getDefaultProperties().getProperty(DefaultScoringAlgorithm.POINTS_PER_NO)); XMLUtilities.addChild(memento, "started", running); XMLUtilities.addChild(memento, "starttime", formattedSeconds); @@ -396,7 +398,7 @@ public XMLMemento createElement(IInternalContest contest, Language language, int /** * Add Language fields. - * + * *

          * <language>
          * <name>C++</name>
    @@ -428,7 +430,7 @@ public XMLMemento createElement(IInternalContest contest, Problem problem, int i
     
         /**
          * Add problem fields.
    -     * 
    +     *
          * 
          * <problem id="1" state="enabled">
          * <label>A</label>
    @@ -436,7 +438,7 @@ public XMLMemento createElement(IInternalContest contest, Problem problem, int i
          * <balloon-color rgb="#ffff00">yellow</balloon-color>
          * </problem>
          * 
    - * + * * @param memento * @param contest * @param problem @@ -463,7 +465,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Problem p return memento; } - + private String getColorRGB(IInternalContest contest, Problem problem) { BalloonSettings settings = getColorSettings(contest); @@ -501,7 +503,7 @@ protected BalloonSettings getColorSettings(IInternalContest contest) { public String getColorsFilename() { return colorsFilename; } - + public void setColorsFilename(String colorsFilename) { this.colorsFilename = colorsFilename; } @@ -579,9 +581,9 @@ private String getColor(IInternalContest contest, Problem problem) { /** * For the input number, returns an uppercase letter. - * + * * 1 = A, 2 = B, etc. - * + * * @param id problem number, based at one. * @return single upper case letter. */ @@ -593,9 +595,9 @@ protected String getProblemLetter(int id) { /** * This routine checks and obeys the preliminary judgement rules. - * + * * @param run - * @param sendNotificationsForPreliminary + * @param sendNotificationsForPreliminary * @return true if run is judged and the state is valid */ public boolean isValidJudgement(Run run, boolean sendNotificationsForPreliminary) { @@ -621,9 +623,9 @@ public boolean isValidJudgement(Run run, boolean sendNotificationsForPreliminary /** * Should a balloon be issued for this run? - * + * * @param run - * @param contest + * @param contest * @return true if valid */ public boolean isValidRun(IInternalContest contest, Run run) { @@ -645,7 +647,7 @@ public boolean isValidRun(IInternalContest contest, Run run) { /** * Return list all problems that team has solved. - * + * * @param contest * @param id * @return @@ -669,7 +671,7 @@ public boolean isValidRun(IInternalContest contest, Run run) { } } - Problem [] problems = (Problem[]) probVector.toArray(new Problem[probVector.size()]); + Problem [] problems = probVector.toArray(new Problem[probVector.size()]); Arrays.sort(problems, new ProblemComparator(contest)); return problems; } @@ -730,7 +732,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Notificat memento.putInteger("team-id", clientId.getClientNumber()); XMLUtilities.addChild(memento, "team-id", clientId.getClientNumber()); - + XMLUtilities.addChild(memento, "contest-time", XMLUtilities.formatSeconds(notification.getElapsedMS())); XMLUtilities.addChild(memento, "time", XMLUtilities.formatSeconds(notification.getElapsedMS())); @@ -768,7 +770,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Notificat addBalloonMemento(memento, contest, problem); } - return memento; + return memento; } @@ -841,7 +843,7 @@ public XMLMemento createElement(IInternalContest contest, Account account) { /** * Create account memento. - * + * *
          * <team id="1" external-id="23412">
          * <name>American University of Beirut</name>
    @@ -867,20 +869,53 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Account a
             XMLUtilities.addChild(memento, "university", account.getDisplayName());
     
             try {
    +            // For legacy stuff, use primary group id supplied by CMS
                 String regionName = "";
    -            if (account.getGroupId() != null) {
    -                Group group = contest.getGroup(account.getGroupId());
    +            if (account.getPrimaryGroupId() != null) {
    +                Group group = contest.getGroup(account.getPrimaryGroupId());
                     regionName = group.getDisplayName();
                 }
    -            
    +
                 XMLUtilities.addChild(memento, "region", regionName);
             } catch (Exception e) {
    -            System.out.println("Failed to lookup group for "+account+" group id = "+account.getGroupId());
    +            System.out.println("Failed to lookup group for "+account+" group id = "+account.getPrimaryGroupId());
                 e.printStackTrace();
             }
    +
    +        // Add groups node with list of groups account is a memory of
    +        HashSet groups = account.getGroupIds();
    +        if(groups != null) {
    +            Group group;
    +            for(ElementId groupElementId: groups) {
    +                try {
    +                    group = contest.getGroup(groupElementId);
    +                    if(group != null) {
    +                        addGroupRow(memento, group);
    +                    }
    +                } catch(Exception e) {
    +                    StaticLog.getLog().log(Log.WARNING, "Failed to lookup group for "+account+" group id = "+groupElementId, e);
    +                    e.printStackTrace();
    +                }
    +            }
    +        }
             return memento;
         }
     
    +    /**
    +     * Add XML group info for a team.
    +     *
    +     * @param mementoRoot xml root to add to
    +     * @param group PC2 Group
    +     * @return the xml node
    +     */
    +    private IMemento addGroupRow(IMemento mementoRoot, Group group) {
    +
    +        IMemento groupInfoMemento = mementoRoot.createChild("groupInfo");
    +        groupInfoMemento.putString("teamGroupName", group.getDisplayName());
    +        groupInfoMemento.putInteger("teamGroupExternalId", group.getGroupId());
    +        return groupInfoMemento;
    +    }
    +
         public XMLMemento createElement(IInternalContest contest, RunTestCase testCase, Run run) {
             XMLMemento memento = XMLMemento.createWriteRoot(TESTCASE_TAG);
             addMemento(memento, contest, testCase, run);
    @@ -901,20 +936,20 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
     //        True
     //        2
     //        51
    -        
    +
             Problem problem = contest.getProblem(run.getProblemId());
    -        
    +
             XMLUtilities.addChild(memento, "i", testCase.getTestNumber());
             XMLUtilities.addChild(memento, "judged", run.isJudged());
             XMLUtilities.addChild(memento, "judgement_id", testCase.getTestNumber());
             XMLUtilities.addChild(memento, "n", problem.getNumberTestCases());
    -        
    +
     //        AC
     //        2
     //        True
    -        
    +
             XMLUtilities.addChild(memento, "solved", run.isSolved());
    -        
    +
             // TODO CCS is result really the judgement acronym?
             String result = Judgement.ACRONYM_OTHER_CONTACT_STAFF;
             if (testCase.isPassed()){
    @@ -922,19 +957,19 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
             }
             XMLUtilities.addChild(memento, "result", result);
             XMLUtilities.addChild(memento, "run-id", run.getNumber());
    -        
    +
             // TODO CCS is solve whether the run or the test case was solved?
             /**
              * There may be an implication that if there is no testcase output then
              * the test failed and all remaining test cases failed.
              */
    -  
    +
     //        
     //        1337173290.16
     //       
     //        XMLUtilities.addChild(memento, "contest-time", XMLUtilities.formatSeconds(run.getElapsedMins() * 1000));
             XMLUtilities.addChild(memento, "time", XMLUtilities.formatSeconds(run.getElapsedMS()));
    -        
    +
             /**
              * Real time time stamp.
              * TODO is this the time stamp for the end of the judgement or the time stamp
    @@ -943,7 +978,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
             XMLUtilities.addChild(memento, "timestamp", XMLUtilities.getTimeStamp());
     
             return memento;
    -    }  
    +    }
     
         public XMLMemento createElement(IInternalContest contest, Clarification clarification) {
             XMLMemento memento = XMLMemento.createWriteRoot(CLARIFICATION_TAG);
    @@ -1004,7 +1039,7 @@ private int getProblemIndex(IInternalContest contest, Problem inProblem) {
     
         /**
          * Create RUN XML element.
    -     * 
    +     *
          * @param contest
          * @param run
          * @param suppressJudgement if true, do not output judgement information.
    @@ -1018,7 +1053,7 @@ public XMLMemento createElement(IInternalContest contest, Run run, boolean suppr
     
         /**
          * Add RUN element.
    -     * 
    +     *
          * @param memento
          * @param contest
          * @param run
    @@ -1063,15 +1098,15 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run,
     //        C++
     //        True
     //        4
    -        
    +
     //        WA
     //        False
     //        74
     //        
     //        1265353100.29
     //        
    -        
    -        
    +
    +
     //        memento.putInteger("id", run.getNumber());
             XMLUtilities.addChild(memento, "id", run.getNumber());
             if (suppressJudgement) {
    @@ -1081,22 +1116,22 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run,
             }
             Language language = contest.getLanguage(run.getLanguageId());
             XMLUtilities.addChild(memento, "language", language.getDisplayName());
    -        
    +
             if ((!run.isSolved()) && isYoungerThanFirstYes(contest, run)) {
                 // If this a "no" run and run is younger than first yes.
                 XMLUtilities.addChild(memento, "penalty", "True");
             } else {
                 XMLUtilities.addChild(memento, "penalty", "False");
             }
    -        
    +
             Problem problem = contest.getProblem(run.getProblemId());
             int problemIndex = getProblemIndex(contest, problem);
     //        memento.putInteger("problem", problemIndex);
             XMLUtilities.addChild(memento, "problem", problemIndex);
    -        
    +
             XMLUtilities.addChild(memento, "team-id",  run.getSubmitter().getClientNumber());
             XMLUtilities.addChild(memento, "team",  run.getSubmitter().getClientNumber());
    -        
    +
             memento.putInteger("team-id", run.getSubmitter().getClientNumber());
     
             if ((!suppressJudgement) && run.isJudged()) {
    @@ -1107,7 +1142,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run,
                 }
                 XMLUtilities.addChild(memento, "result", acronym);
             }
    -        
    +
             XMLUtilities.addChild(memento, "solved", run.isSolved());
     
             XMLUtilities.addChild(memento, "elapsed-Mins", run.getElapsedMins());
    @@ -1125,14 +1160,14 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run,
          * @return true if run is younger than first yes, false if no solution or run is actually younger than input solved run.
          */
         protected boolean isYoungerThanFirstYes(IInternalContest contest, Run run) {
    -        
    +
             Run firstYes = findFistYes (contest, run.getSubmitter(), run.getProblemId());
    -        
    +
             if (firstYes == null){
                 // no solution found
                 return false;
             } else {
    -            return run.getElapsedMS() < firstYes.getElapsedMS(); 
    +            return run.getElapsedMS() < firstYes.getElapsedMS();
             }
         }
     
    @@ -1142,9 +1177,9 @@ protected boolean isYoungerThanFirstYes(IInternalContest contest, Run run) {
          */
         protected Run findFistYes(IInternalContest contest, ClientId submitter, ElementId problemId) {
             Run[] runs = contest.getRuns();
    -        
    +
             Run lastYesRun = null;
    -        
    +
             for (Run run : runs) {
                 if (run.isSolved()){
                     if (run.getProblemId().equals(problemId)){
    @@ -1161,9 +1196,9 @@ protected Run findFistYes(IInternalContest contest, ClientId submitter, ElementI
                     }
                 }
             }
    -        
    +
             return lastYesRun;
    -        
    +
         }
     
         /**
    @@ -1177,14 +1212,14 @@ protected String toXML(XMLMemento mementoRoot)  {
                 return "";
             }
         }
    -    
    +
         public String createStartupXML(IInternalContest contest) {
             return createStartupXML(contest, new Filter());
         }
     
         /**
          * Starts contest XML and adds all configuration data/values.
    -     * 
    +     *
          * @param contest
          * @param filter
          * @return
    @@ -1213,7 +1248,7 @@ public String createStartupXML(IInternalContest contest, Filter filter) {
                 }
                 idx++;
             }
    -        
    +
             Group[] groups = contest.getGroups();
             Arrays.sort(groups, new GroupComparator());
             for (Group group : groups) {
    @@ -1240,7 +1275,7 @@ public String createStartupXML(IInternalContest contest, Filter filter) {
             Arrays.sort(runs, new RunComparator());
     
             for (Run run : runs) {
    -            
    +
                 if (teamDisplayedOnScoreboard(contest, run.getSubmitter())){
     
                     if (filter.matches(run)) {
    @@ -1281,21 +1316,21 @@ public XMLMemento createElement(IInternalContest contest, Group group, int idx)
          * @param run
          */
         protected RunTestCase[] getLastJudgementTestCases(Run run) {
    -        
    +
             ArrayList  cases = new ArrayList();
    -        
    +
             if (run.isJudged()){
                 RunTestCase[] runTestCases = run.getRunTestCases();
                 JudgementRecord judgementRecord = run.getJudgementRecord();
    -            
    +
                 for (RunTestCase runTestCaseResult : runTestCases) {
                     if (runTestCaseResult.matchesJudgement(judgementRecord)){
                         cases.add(runTestCaseResult);
                     }
                 }
             }
    -        
    -        return (RunTestCase[]) cases.toArray(new RunTestCase[cases.size()]);
    +
    +        return cases.toArray(new RunTestCase[cases.size()]);
         }
     
         /**
    @@ -1304,7 +1339,7 @@ protected RunTestCase[] getLastJudgementTestCases(Run run) {
          */
         public Account[] getTeamAccounts(IInternalContest inContest) {
             Vector accountVector = inContest.getAccounts(ClientType.Type.TEAM);
    -        Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]);
    +        Account[] accounts = accountVector.toArray(new Account[accountVector.size()]);
             Arrays.sort(accounts, new AccountComparator());
     
             return accounts;
    @@ -1316,7 +1351,7 @@ public String createFinalizeXML(IInternalContest contest, FinalizeData data) {
     
             XMLMemento memento = XMLMemento.createWriteRoot(FINALIZE_TAG);
     
    -        addMemento (memento, contest, data); 
    +        addMemento (memento, contest, data);
     
             sb.append(toXML(memento));
     
    @@ -1365,7 +1400,7 @@ public XMLMemento createElement(IInternalContest contest, Judgement judgement) {
             addMemento(memento, contest, judgement);
             return memento;
         }
    -    
    +
         private String guestAcronym(Judgement judgement) {
             if (judgement.getAcronym() == null || judgement.getAcronym().length() == 0) {
                 return guessAcronym(judgement.getDisplayName());
    @@ -1382,14 +1417,14 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Judgement
             String name = judgement.getDisplayName();
     
             String acronym = guestAcronym(judgement);
    -        
    -        XMLUtilities.addChild(memento, "acronym", acronym); 
    +
    +        XMLUtilities.addChild(memento, "acronym", acronym);
             XMLUtilities.addChild(memento, "name", name);
             return memento;
         }
     
         public XMLMemento createElement(IInternalContest contest, JudgementRecord judgementRecord) {
    -        // Judgement Record 
    +        // Judgement Record
             // 
             // CE
             // Compile Error
    diff --git a/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013.java b/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013.java
    index 7e7587315..4e78035d7 100644
    --- a/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013.java
    +++ b/src/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013.java
    @@ -1,4 +1,4 @@
    -// 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.exports.ccs;
     
     import java.io.IOException;
    @@ -43,14 +43,14 @@
      * Event Feed Finals 2013 XML.
      *
      * The CCS Event Feed is implemented in the {@link EventFeedXML}.
    - * 
    + *
      * Mementos are the internal tags for a element, Elements are the XML Elements with
      * surrounding tag.
      * 

    * For example {@link #createElement(IInternalContest, Language, int)} will create * an element with a {@link #LANGUAGE_TAG} whereas the contents of the memento * {@link #addMemento(IMemento, IInternalContest, Language, int)}. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -83,15 +83,15 @@ public class EventFeedXML2013 { public static final String JUDGEMENT_RECORD_TAG = "judgement_record"; private RunComparator runComparator = new RunComparator(); - + private VersionInfo versionInfo = new VersionInfo(); - + private Log log = null; - + public String toXML(IInternalContest contest) { return toXML(contest, new Filter()); } - + /** * Return freeze XML. * @param contest @@ -99,14 +99,14 @@ public String toXML(IInternalContest contest) { * @return */ public String toXMLFreeze(IInternalContest contest, long minutesFromEnd) { - + long mins = contest.getContestTime().getContestLengthMins() - minutesFromEnd; - + Filter filter = new Filter(); filter.setFilteringDeleted(true); // only get XML elements for events before mins filter.setEndElapsedTime(mins); - + return toXML(contest, filter); } @@ -114,7 +114,7 @@ public String toXMLFreeze(IInternalContest contest, long minutesFromEnd) { public String toXML(IInternalContest contest, Filter filter) { filter.setFilteringDeleted(true); - + XMLMemento mementoRoot = XMLMemento.createWriteRoot(CONTEST_TAG); IMemento memento = mementoRoot.createChild(INFO_TAG); @@ -133,7 +133,7 @@ public String toXML(IInternalContest contest, Filter filter) { memento = mementoRoot.createChild(JUDGEMENT_TAG); addMemento(memento, contest, judgement, sequenceNumber); sequenceNumber++; - } + } Language[] languages = contest.getLanguages(); int num = 1; @@ -157,7 +157,7 @@ public String toXML(IInternalContest contest, Filter filter) { Vector teams = contest.getAccounts(Type.TEAM); - Account[] accounts = (Account[]) teams.toArray(new Account[teams.size()]); + Account[] accounts = teams.toArray(new Account[teams.size()]); Arrays.sort(accounts, new AccountComparator()); for (Account account : accounts) { @@ -174,7 +174,7 @@ public String toXML(IInternalContest contest, Filter filter) { if (filter.matches(run)) { memento = mementoRoot.createChild(RUN_TAG); addMemento(memento, contest, run); // add RUN - + RunTestCase[] runTestCases = getLastJudgementTestCases(run); Arrays.sort(runTestCases, new RunTestCaseComparator()); for (RunTestCase runTestCaseResult : runTestCases) { @@ -203,7 +203,7 @@ public String toXML(IInternalContest contest, Filter filter) { addMemento(memento, contest, clarification); } } - + FinalizeData finalizeData = contest.getFinalizeData(); if (finalizeData != null) { if (finalizeData.isCertified()) { @@ -221,7 +221,7 @@ private String getXMLFooterComment() { /** * return first run for client and problem. - * + * * @param contest * @param clientId * @param problemId @@ -244,7 +244,7 @@ public Run getFirstSolvedRun(IInternalContest contest, ClientId clientId, Elemen /** * create info XML element - * + * * @param contest * @param filter * @return @@ -279,7 +279,7 @@ public IMemento addInfoMemento(IMemento memento, IInternalContest contest, Conte } } - XMLUtilities.addChild(memento, "length", contestLengthString); + XMLUtilities.addChild(memento, "length", contestLengthString); XMLUtilities.addChild(memento, "penalty", DefaultScoringAlgorithm.getDefaultProperties().getProperty(DefaultScoringAlgorithm.POINTS_PER_NO)); XMLUtilities.addChild(memento, "started", titleCaseBoolean(running)); XMLUtilities.addChild(memento, "starttime", formattedSeconds); @@ -301,7 +301,7 @@ public XMLMemento createElement(IInternalContest contest, Language language, int /** * Add Language fields. - * + * *

          * <language>
          * <name>C++</name>
    @@ -333,7 +333,7 @@ public XMLMemento createElement(IInternalContest contest, Problem problem, int i
     
         /**
          * Add problem fields.
    -     * 
    +     *
          * 
          * <problem id="1" state="enabled">
          * <label>A</label>
    @@ -341,7 +341,7 @@ public XMLMemento createElement(IInternalContest contest, Problem problem, int i
          * <balloon-color rgb="#ffff00">yellow</balloon-color>
          * </problem>
          * 
    - * + * * @param memento * @param contest * @param problem @@ -354,7 +354,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Problem p // 1 // Self-Assembly // - + memento.createChildNode("id", Integer.toString(id)); memento.createChildNode("name", problem.toString()); // this is not currently in the CCS spec for EventFeed @@ -370,9 +370,9 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Problem p /** * For the input number, returns an uppercase letter. - * + * * 1 = A, 2 = B, etc. - * + * * @param id problem number, based at one. * @return single upper case letter. */ @@ -384,9 +384,9 @@ protected String getProblemLetter(int id) { /** * This routine checks and obeys the preliminary judgement rules. - * + * * @param run - * @param sendNotificationsForPreliminary + * @param sendNotificationsForPreliminary * @return true if run is judged and the state is valid */ public boolean isValidJudgement(Run run, boolean sendNotificationsForPreliminary) { @@ -412,9 +412,9 @@ public boolean isValidJudgement(Run run, boolean sendNotificationsForPreliminary /** * Should a balloon be issued for this run? - * + * * @param run - * @param contest + * @param contest * @return true if valid */ public boolean isValidRun(IInternalContest contest, Run run) { @@ -436,7 +436,7 @@ public boolean isValidRun(IInternalContest contest, Run run) { /** * Return list all problems that team has solved. - * + * * @param contest * @param id * @return @@ -460,7 +460,7 @@ public boolean isValidRun(IInternalContest contest, Run run) { } } - Problem [] problems = (Problem[]) probVector.toArray(new Problem[probVector.size()]); + Problem [] problems = probVector.toArray(new Problem[probVector.size()]); Arrays.sort(problems, new ProblemComparator(contest)); return problems; } @@ -473,7 +473,7 @@ public XMLMemento createElement(IInternalContest contest, Account account) { /** * Create account memento. - * + * *
          * <team id="1" external-id="23412">
          * <name>American University of Beirut</name>
    @@ -488,7 +488,7 @@ public XMLMemento createElement(IInternalContest contest, Account account) {
          * @return
          */
         public IMemento addMemento(IMemento memento, IInternalContest contest, Account account) {
    -        
    +
     //        
     //        171936
     //        107
    @@ -500,18 +500,18 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Account a
     
     
             int teamId =  account.getClientId().getClientNumber();
    -        
    +
             XMLUtilities.addChild(memento, "external-id", useDefaultIfEmpty (account.getExternalId(),"836577"+teamId));
             XMLUtilities.addChild(memento, "id", teamId);
             XMLUtilities.addChild(memento, "name", account.getDisplayName());
    -        
    +
             XMLUtilities.addChild(memento, "nationality", account.getCountryCode());
     
             String regionName = getRegionName(contest, account);
             XMLUtilities.addChild(memento, "region", regionName);
    -        
    +
             XMLUtilities.addChild(memento, "university", account.getDisplayName());
    -        
    +
             return memento;
         }
     
    @@ -530,20 +530,20 @@ private String useDefaultIfEmpty(String value, String defaultString) {
         }
     
         private String getRegionName(IInternalContest contest, Account account) {
    -        
    +
             /**
              * This code is in place because sometimes groupId is null.
              */
    -        
    +
             String regionName = "";
     
             try {
    -            if (account.getGroupId() != null) {
    -                Group group = contest.getGroup(account.getGroupId());
    +            if (account.getPrimaryGroupId() != null) {
    +                Group group = contest.getGroup(account.getPrimaryGroupId());
                     regionName = group.getDisplayName();
                 }
             } catch (Exception e) {
    -            System.out.println("Failed to lookup group for "+account+" group id = "+account.getGroupId());
    +            System.out.println("Failed to lookup group for "+account+" group id = "+account.getPrimaryGroupId());
                 e.printStackTrace();
             }
     
    @@ -570,18 +570,18 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
     //        True
     //        78
     //        57
    -        
    +
             Problem problem = contest.getProblem(run.getProblemId());
    -        
    +
             XMLUtilities.addChild(memento, "i", testCase.getTestNumber());
             XMLUtilities.addChild(memento, "judged", titleCaseBoolean(run.isJudged()));
             XMLUtilities.addChild(memento, "judgement_id", run.getNumber());
             XMLUtilities.addChild(memento, "n", problem.getNumberTestCases());
    -        
    +
     //        AC
     //        78
     //        True
    -        
    +
             String result = Judgement.ACRONYM_JUDGING_ERROR;
             if (testCase.isPassed()){
                 result = Judgement.ACRONYM_ACCEPTED;
    @@ -589,7 +589,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
             XMLUtilities.addChild(memento, "result", result);
             XMLUtilities.addChild(memento, "run-id", run.getNumber());
             XMLUtilities.addChild(memento, "solved", titleCaseBoolean (testCase.isPassed()));
    -        
    +
     //        
     //        1372831837.31
     //       
    @@ -597,7 +597,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
             XMLUtilities.addChild(memento, "timestamp", XMLUtilities.getTimeStamp());
     
             return memento;
    -    }  
    +    }
     
         public XMLMemento createElement(IInternalContest contest, Clarification clarification) {
             XMLMemento memento = XMLMemento.createWriteRoot(CLARIFICATION_TAG);
    @@ -621,7 +621,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Clarifica
             XMLUtilities.addChild(memento, "id", clarification.getNumber());
             XMLUtilities.addChild(memento, "answered", titleCaseBoolean(clarification.isAnswered()));
             XMLUtilities.addChild(memento, "question", clarification.getQuestion());
    -        
    +
     //        fresh
     //        53
     //        
    @@ -630,14 +630,14 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Clarifica
             XMLUtilities.addChild(memento, "status", status);
             XMLUtilities.addChild(memento, "team", clarification.getSubmitter().getClientNumber());
             XMLUtilities.addChild(memento, "time", XMLUtilities.formatSeconds(clarification.getElapsedMS()));
    -        
    +
     //        1372832394.63
     //        False
     //       
     //       
             XMLUtilities.addChild(memento, "timestamp", XMLUtilities.getTimeStamp());
             XMLUtilities.addChild(memento, "to-all", titleCaseBoolean(clarification.isSendToAll()));
    -        
    +
             return memento;
         }
     
    @@ -669,7 +669,7 @@ private int getProblemIndex(IInternalContest contest, Problem inProblem) {
     
         /**
          * Create RUN XML element.
    -     * 
    +     *
          * @param contest the contest which the run belongs
          * @param run the Run to be contained in the XML
          * @return the created XMLMemento
    @@ -682,7 +682,7 @@ public XMLMemento createElement(IInternalContest contest, Run run) {
     
         /**
          * Add RUN element.
    -     * 
    +     *
          * @param memento
          * @param contest
          * @param run
    @@ -696,37 +696,37 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run)
     //        C++
     //        True
     //        9
    -        
    +
             XMLUtilities.addChild(memento, "id", run.getNumber());
             XMLUtilities.addChild(memento, "judged", titleCaseBoolean(run.isJudged()));
     
             Language language = contest.getLanguage(run.getLanguageId());
             XMLUtilities.addChild(memento, "language", language.getDisplayName());
    -        
    +
             if ((!run.isSolved()) && isYoungerThanFirstYes(contest, run)) {
                 // If this a "no" run and run is younger than first yes.
                 XMLUtilities.addChild(memento, "penalty", "True");
             } else {
                 XMLUtilities.addChild(memento, "penalty", "False");
             }
    -        
    +
             Problem problem = contest.getProblem(run.getProblemId());
             int problemIndex = getProblemIndex(contest, problem);
             XMLUtilities.addChild(memento, "problem", problemIndex);
    -        
    +
     //        TLE
     //        False
     //        done
    -        
    +
             if (run.isJudged()){
                 Judgement judgement = contest.getJudgement(run.getJudgementRecord().getJudgementId());
                 String acronym = getAcronym(judgement);
                 XMLUtilities.addChild(memento, "result", acronym);
             }
    -        
    +
             XMLUtilities.addChild(memento, "solved", run.isSolved());
             XMLUtilities.addChild(memento, "status", getStatus(run.isJudged()));
    -        
    +
     //        50
     //        
     //        1372849248.21
    @@ -746,14 +746,14 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run)
          * @return true if run is younger than first yes, false if no solution or run is actually younger than input solved run.
          */
         protected boolean isYoungerThanFirstYes(IInternalContest contest, Run run) {
    -        
    +
             Run firstYes = findFistYes (contest, run.getSubmitter(), run.getProblemId());
    -        
    +
             if (firstYes == null){
                 // no solution found
                 return false;
             } else {
    -            return run.getElapsedMS() < firstYes.getElapsedMS(); 
    +            return run.getElapsedMS() < firstYes.getElapsedMS();
             }
         }
     
    @@ -763,9 +763,9 @@ protected boolean isYoungerThanFirstYes(IInternalContest contest, Run run) {
          */
         protected Run findFistYes(IInternalContest contest, ClientId submitter, ElementId problemId) {
             Run[] runs = contest.getRuns();
    -        
    +
             Run lastYesRun = null;
    -        
    +
             for (Run run : runs) {
                 if (run.isSolved()){
                     if (run.getProblemId().equals(problemId)){
    @@ -782,9 +782,9 @@ protected Run findFistYes(IInternalContest contest, ClientId submitter, ElementI
                     }
                 }
             }
    -        
    +
             return lastYesRun;
    -        
    +
         }
     
         /**
    @@ -798,7 +798,7 @@ protected String toXML(XMLMemento mementoRoot)  {
                 return "";
             }
         }
    -    
    +
         public String createStartupXML(IInternalContest contest) {
             Filter filter = new Filter();
             filter.setFilteringDeleted(true);
    @@ -807,7 +807,7 @@ public String createStartupXML(IInternalContest contest) {
     
         /**
          * Starts contest XML and adds all configuration data/values.
    -     * 
    +     *
          * @param contest
          * @param judgement
          * @return
    @@ -833,7 +833,7 @@ public String createStartupXML(IInternalContest contest, Filter filter) {
                 }
                 idx++;
             }
    -        
    +
             Group[] groups = contest.getGroups();
             Arrays.sort(groups, new GroupComparator());
             for (Group group : groups) {
    @@ -865,7 +865,7 @@ public String createStartupXML(IInternalContest contest, Filter filter) {
     
                 if (filter.matches(run)) {
                     sb.append(toXML(createElement(contest, run))); // add RUN
    -                
    +
                     RunTestCase[] runTestCases = getLastJudgementTestCases(run);
                     Arrays.sort(runTestCases, new RunTestCaseComparator());
                     for (RunTestCase runTestCaseResult : runTestCases) {
    @@ -896,21 +896,21 @@ public XMLMemento createElement(IInternalContest contest, Group group, int idx)
          * @param run
          */
         protected RunTestCase[] getLastJudgementTestCases(Run run) {
    -        
    +
             ArrayList  cases = new ArrayList();
    -        
    +
             if (run.isJudged()){
                 RunTestCase[] runTestCases = run.getRunTestCases();
                 JudgementRecord judgementRecord = run.getJudgementRecord();
    -            
    +
                 for (RunTestCase runTestCaseResult : runTestCases) {
                     if (runTestCaseResult.matchesJudgement(judgementRecord)){
                         cases.add(runTestCaseResult);
                     }
                 }
             }
    -        
    -        return (RunTestCase[]) cases.toArray(new RunTestCase[cases.size()]);
    +
    +        return cases.toArray(new RunTestCase[cases.size()]);
         }
     
         /**
    @@ -919,7 +919,7 @@ protected RunTestCase[] getLastJudgementTestCases(Run run) {
          */
         public Account[] getTeamAccounts(IInternalContest inContest) {
             Vector accountVector = inContest.getAccounts(ClientType.Type.TEAM);
    -        Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]);
    +        Account[] accounts = accountVector.toArray(new Account[accountVector.size()]);
             Arrays.sort(accounts, new AccountComparator());
     
             return accounts;
    @@ -931,7 +931,7 @@ public String createFinalizeXML(IInternalContest contest, FinalizeData data) {
     
             XMLMemento memento = XMLMemento.createWriteRoot(FINALIZE_TAG);
     
    -        addMemento (memento, contest, data); 
    +        addMemento (memento, contest, data);
     
             sb.append(toXML(memento));
     
    @@ -967,7 +967,7 @@ public XMLMemento createElement(IInternalContest contest, Group group) {
         }
     
         public void addMemento(IMemento memento, IInternalContest contest, Group group) {
    -        
    +
     //        
     //        5752
     //        South Pacific
    @@ -982,7 +982,7 @@ public XMLMemento createElement(IInternalContest contest, Judgement judgement, i
             addMemento(memento, contest, judgement, sequenceNumber);
             return memento;
         }
    -    
    +
         private String getAcronym(Judgement judgement) {
     
             if (Judgement.ACRONYM_OTHER_CONTACT_STAFF.equals(judgement.getAcronym())) {
    @@ -997,7 +997,7 @@ private String getAcronym(Judgement judgement) {
         }
     
         public IMemento addMemento(IMemento memento, IInternalContest contest, Judgement judgement, int judgementSequence) {
    -        
    +
     //        
     //        CE
     //        8
    @@ -1006,15 +1006,15 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Judgement
     
             String name = judgement.getDisplayName();
             String acronym = getAcronym(judgement);
    -        
    -        XMLUtilities.addChild(memento, "acronym", acronym); 
    -        XMLUtilities.addChild(memento, "id", judgementSequence); 
    +
    +        XMLUtilities.addChild(memento, "acronym", acronym);
    +        XMLUtilities.addChild(memento, "id", judgementSequence);
             XMLUtilities.addChild(memento, "name", name);
             return memento;
         }
     
         public XMLMemento createElement(IInternalContest contest, JudgementRecord judgementRecord) {
    -        // Judgement Record 
    +        // Judgement Record
             // 
             // CE
             // Compile Error
    diff --git a/src/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXML.java b/src/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXML.java
    index b4e47143c..2565efb08 100644
    --- a/src/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXML.java
    +++ b/src/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXML.java
    @@ -1,4 +1,4 @@
    -// 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.exports.ccs;
     
     import java.io.IOException;
    @@ -44,14 +44,14 @@
      * ICPC Tools Resolver Event Feed XML.
      *
      * The CCS Event Feed is implemented in the {@link EventFeedXML}.
    - * 
    + *
      * Mementos are the internal tags for a element, Elements are the XML Elements with
      * surrounding tag.
      * 

    * For example {@link #createElement(IInternalContest, Language, int)} will create * an element with a {@link #LANGUAGE_TAG} whereas the contents of the memento * {@link #addMemento(IMemento, IInternalContest, Language, int)}. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -84,17 +84,17 @@ public class ResolverEventFeedXML { public static final String JUDGEMENT_RECORD_TAG = "judgement_record"; private RunComparator runComparator = new RunComparator(); - + private VersionInfo versionInfo = new VersionInfo(); - + private Log log = null; - + private static String contestId = "unset"; - + public String toXML(IInternalContest contest) { return toXML(contest, new Filter()); } - + /** * Return freeze XML. * @param contest @@ -102,14 +102,14 @@ public String toXML(IInternalContest contest) { * @return */ public String toXMLFreeze(IInternalContest contest, long minutesFromEnd) { - + long mins = contest.getContestTime().getContestLengthMins() - minutesFromEnd; - + Filter filter = new Filter(); filter.setFilteringDeleted(true); // only get XML elements for events before mins filter.setEndElapsedTime(mins); - + return toXML(contest, filter); } @@ -117,7 +117,7 @@ public String toXMLFreeze(IInternalContest contest, long minutesFromEnd) { public String toXML(IInternalContest contest, Filter filter) { filter.setFilteringDeleted(true); - + XMLMemento mementoRoot = XMLMemento.createWriteRoot(CONTEST_TAG); IMemento memento = mementoRoot.createChild(INFO_TAG); @@ -136,7 +136,7 @@ public String toXML(IInternalContest contest, Filter filter) { memento = mementoRoot.createChild(JUDGEMENT_TAG); addMemento(memento, contest, judgement, sequenceNumber); sequenceNumber++; - } + } Language[] languages = contest.getLanguages(); int num = 1; @@ -160,7 +160,7 @@ public String toXML(IInternalContest contest, Filter filter) { Vector teams = contest.getAccounts(Type.TEAM); - Account[] accounts = (Account[]) teams.toArray(new Account[teams.size()]); + Account[] accounts = teams.toArray(new Account[teams.size()]); Arrays.sort(accounts, new AccountComparator()); for (Account account : accounts) { @@ -186,7 +186,7 @@ public String toXML(IInternalContest contest, Filter filter) { if (filter.matches(run)) { memento = mementoRoot.createChild(RUN_TAG); addMemento(memento, contest, run); // add RUN - + RunTestCase[] runTestCases = getLastJudgementTestCases(run); Arrays.sort(runTestCases, new RunTestCaseComparator()); for (RunTestCase runTestCaseResult : runTestCases) { @@ -216,7 +216,7 @@ public String toXML(IInternalContest contest, Filter filter) { addMemento(memento, contest, clarification); } } - + FinalizeData finalizeData = contest.getFinalizeData(); if (finalizeData != null) { if (finalizeData.isCertified()) { @@ -234,7 +234,7 @@ private String getXMLFooterComment() { /** * return first run for client and problem. - * + * * @param contest * @param clientId * @param problemId @@ -257,7 +257,7 @@ public Run getFirstSolvedRun(IInternalContest contest, ClientId clientId, Elemen /** * create info XML element. - * + * * @param contest * @param filter * @return @@ -295,12 +295,12 @@ public IMemento addInfoMemento(IMemento memento, IInternalContest contest, Conte formattedSeconds = XMLUtilities.formatSeconds(time.getContestStartTime().getTimeInMillis()); } } - + if (info.getScheduledStartDate() != null) { formattedSeconds = XMLUtilities.formatSeconds(info.getScheduledStartDate().getTime()); } - XMLUtilities.addChild(memento, "length", contestLengthString); + XMLUtilities.addChild(memento, "length", contestLengthString); XMLUtilities.addChild(memento, "penalty", DefaultScoringAlgorithm.getDefaultProperties().getProperty(DefaultScoringAlgorithm.POINTS_PER_NO)); XMLUtilities.addChild(memento, "started", titleCaseBoolean(started)); XMLUtilities.addChild(memento, "running", titleCaseBoolean(running)); @@ -313,7 +313,7 @@ public IMemento addInfoMemento(IMemento memento, IInternalContest contest, Conte shortTitle = info.getContestTitle(); } XMLUtilities.addChild(memento, "short-title", shortTitle); - + String myContestId = contest.getContestIdentifier(); if (myContestId != null && !myContestId.equals("")) { contestId = myContestId; @@ -324,10 +324,10 @@ public IMemento addInfoMemento(IMemento memento, IInternalContest contest, Conte contestId = uuid.toString(); } XMLUtilities.addChild(memento, "contest-id", contestId.toLowerCase()); - + String scoreboardFreezeLength = info.getFreezeTime(); XMLUtilities.addChild(memento, "scoreboard-freeze-length", scoreboardFreezeLength); - + //TODO: generate a JUnit test for consistency of the entire element return memento; } @@ -346,7 +346,7 @@ public XMLMemento createElement(IInternalContest contest, Language language, int /** * Add Language fields. - * + * *

          * <language>
          * <name>C++</name>
    @@ -359,9 +359,9 @@ public XMLMemento createElement(IInternalContest contest, Language language, int
          * @return
          */
         public IMemento addMemento(IMemento memento, IInternalContest contest, Language language, int id) {
    -        
    +
             // TODO CCS validate content vs demo/sample XML
    -        
    +
     
     //        
     //        1
    @@ -381,7 +381,7 @@ public XMLMemento createElement(IInternalContest contest, Problem problem, int i
     
         /**
          * Add problem fields.
    -     * 
    +     *
          * 
          * <problem id="1" state="enabled">
          * <label>A</label>
    @@ -389,7 +389,7 @@ public XMLMemento createElement(IInternalContest contest, Problem problem, int i
          * <balloon-color rgb="#ffff00">yellow</balloon-color>
          * </problem>
          * 
    - * + * * @param memento * @param contest * @param problem @@ -397,15 +397,15 @@ public XMLMemento createElement(IInternalContest contest, Problem problem, int i * @return */ public IMemento addMemento(IMemento memento, IInternalContest contest, Problem problem, int id) { - + // TODO CCS validate content vs demo/sample XML - + // // 1 // Self-Assembly // - + memento.createChildNode("id", Integer.toString(id)); memento.createChildNode("name", problem.toString()); // added in wf2016 version @@ -439,9 +439,9 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Problem p /** * For the input number, returns an uppercase letter. - * + * * 1 = A, 2 = B, etc. - * + * * @param id problem number, based at one. * @return single upper case letter. */ @@ -453,9 +453,9 @@ protected String getProblemLetter(int id) { /** * This routine checks and obeys the preliminary judgement rules. - * + * * @param run - * @param sendNotificationsForPreliminary + * @param sendNotificationsForPreliminary * @return true if run is judged and the state is valid */ public boolean isValidJudgement(Run run, boolean sendNotificationsForPreliminary) { @@ -481,9 +481,9 @@ public boolean isValidJudgement(Run run, boolean sendNotificationsForPreliminary /** * Should a balloon be issued for this run? - * + * * @param run - * @param contest + * @param contest * @return true if valid */ public boolean isValidRun(IInternalContest contest, Run run) { @@ -505,7 +505,7 @@ public boolean isValidRun(IInternalContest contest, Run run) { /** * Return list all problems that team has solved. - * + * * @param contest * @param id * @return @@ -529,7 +529,7 @@ public boolean isValidRun(IInternalContest contest, Run run) { } } - Problem [] problems = (Problem[]) probVector.toArray(new Problem[probVector.size()]); + Problem [] problems = probVector.toArray(new Problem[probVector.size()]); Arrays.sort(problems, new ProblemComparator(contest)); return problems; } @@ -542,7 +542,7 @@ public XMLMemento createElement(IInternalContest contest, Account account) { /** * Create account memento. - * + * *
          * <team id="1" external-id="23412">
          * <name>American University of Beirut</name>
    @@ -557,9 +557,9 @@ public XMLMemento createElement(IInternalContest contest, Account account) {
          * @return
          */
         public IMemento addMemento(IMemento memento, IInternalContest contest, Account account) {
    -        
    +
             // TODO CCS validate content vs demo/sample XML
    -        
    +
     //        
     //        171936
     //        107
    @@ -578,18 +578,18 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Account a
     //    Open Division
     //    University of Melbourne, Australia
     //  
    -        
    +
             int teamId =  account.getClientId().getClientNumber();
    -        
    +
             XMLUtilities.addChild(memento, "external-id", useDefaultIfEmpty(account.getExternalId(), "4242" + teamId));
             XMLUtilities.addChild(memento, "id", teamId);
             XMLUtilities.addChild(memento, "name", account.getDisplayName());
    -        
    +
             XMLUtilities.addChild(memento, "nationality", account.getCountryCode());
     
             String regionName = getRegionName(contest, account);
             XMLUtilities.addChild(memento, "region", regionName);
    -        
    +
             XMLUtilities.addChild(memento, "university", account.getDisplayName());
             XMLUtilities.addChild(memento, "university-short-name", account.getShortSchoolName());
             return memento;
    @@ -610,20 +610,21 @@ private String useDefaultIfEmpty(String value, String defaultString) {
         }
     
         private String getRegionName(IInternalContest contest, Account account) {
    -        
    +
             /**
              * This code is in place because sometimes groupId is null.
              */
    -        
    +
             String regionName = "";
     
             try {
    -            if (account.getGroupId() != null) {
    -                Group group = contest.getGroup(account.getGroupId());
    +            // Since this is a legacy event feed (XML), we only care about the primary group assigned by CMS
    +            if (account.getPrimaryGroupId() != null) {
    +                Group group = contest.getGroup(account.getPrimaryGroupId());
                     regionName = group.getDisplayName();
                 }
             } catch (Exception e) {
    -            System.out.println("Failed to lookup group for "+account+" group id = "+account.getGroupId());
    +            System.out.println("Failed to lookup group for "+account+" group id = "+account.getPrimaryGroupId());
                 e.printStackTrace();
             }
     
    @@ -644,9 +645,9 @@ public XMLMemento createElement(IInternalContest contest, RunTestCase testCase,
          * @param run
          */
         public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCase testCase, Run run) {
    -        
    +
             // TODO CCS validate content vs demo/sample XML
    -        
    +
     
     //        
     //        3
    @@ -654,25 +655,25 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
             // the following is wrong; the CLICS specification requires acronym
     //        78
     //        57
    -        
    +
             Problem problem = contest.getProblem(run.getProblemId());
    -        
    +
             XMLUtilities.addChild(memento, "i", testCase.getTestNumber());
             XMLUtilities.addChild(memento, "judged", titleCaseBoolean(run.isJudged()));
    -        
    +
     //        the following statement is incorrect; see bug 1529
     //        XMLUtilities.addChild(memento, "judgement_id", run.getNumber());
    -        
    +
             //the following replaces the above incorrect statement:
             //output an XML memento containing "acronym", where "acronym" is the acronym associated with the judgement for the test case
             XMLUtilities.addChild(memento, "judgement", contest.getJudgement(testCase.getJudgementId()).getAcronym());
    -        
    +
             XMLUtilities.addChild(memento, "n", problem.getNumberTestCases());
    -        
    +
     //        AC
     //        78
     //        True
    -        
    +
             String result = Judgement.ACRONYM_JUDGING_ERROR;
             if (testCase.isPassed()){
                 result = Judgement.ACRONYM_ACCEPTED;
    @@ -680,7 +681,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
             XMLUtilities.addChild(memento, "result", result);
             XMLUtilities.addChild(memento, "run-id", run.getNumber());
             XMLUtilities.addChild(memento, "solved", titleCaseBoolean (testCase.isPassed()));
    -        
    +
     //        
     //        1372831837.31
     //       
    @@ -688,7 +689,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, RunTestCa
             XMLUtilities.addChild(memento, "timestamp", XMLUtilities.getTimeStamp());
     
             return memento;
    -    }  
    +    }
     
         public XMLMemento createElement(IInternalContest contest, Clarification clarification) {
             XMLMemento memento = XMLMemento.createWriteRoot(CLARIFICATION_TAG);
    @@ -698,7 +699,7 @@ public XMLMemento createElement(IInternalContest contest, Clarification clarific
     
         public IMemento addMemento(IMemento memento, IInternalContest contest, Clarification clarification) {
     
    -        
    +
     //         4/19/2015
     //    
     //    arrow
    @@ -714,7 +715,7 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Clarifica
             XMLUtilities.addChild(memento, "id", clarification.getNumber());
             XMLUtilities.addChild(memento, "answered", titleCaseBoolean(clarification.isAnswered()));
             XMLUtilities.addChild(memento, "question", clarification.getQuestion());
    -        
    +
     //    done
     //    8
     //    
    @@ -723,19 +724,19 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Clarifica
             XMLUtilities.addChild(memento, "status", status);
             XMLUtilities.addChild(memento, "team", clarification.getSubmitter().getClientNumber());
             XMLUtilities.addChild(memento, "time", XMLUtilities.formatSeconds(clarification.getElapsedMS()));
    -        
    +
     //    1414215455.760
     //    False
    -//  C++
     //    True
     //    2
    -        
    +
             XMLUtilities.addChild(memento, "id", run.getNumber());
             XMLUtilities.addChild(memento, "judged", titleCaseBoolean(run.isJudged()));
     
             Language language = contest.getLanguage(run.getLanguageId());
             XMLUtilities.addChild(memento, "language", language.getDisplayName());
    -        
    +
             if ((!run.isSolved()) && isYoungerThanFirstYes(contest, run)) {
                 // If this a "no" run and run is younger than first yes.
                 if (run.isJudged()) {
    @@ -817,15 +818,15 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run)
             } else {
                 XMLUtilities.addChild(memento, "penalty", "False");
             }
    -        
    +
             Problem problem = contest.getProblem(run.getProblemId());
             int problemIndex = getProblemIndex(contest, problem);
             XMLUtilities.addChild(memento, "problem", problemIndex);
    -        
    +
     //    JE
     //    false
     //    done
    -        
    +
             if (run.isJudged()){
                 Judgement judgement = contest.getJudgement(run.getJudgementRecord().getJudgementId());
                 boolean isFinalResult = true;
    @@ -840,19 +841,19 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Run run)
                     XMLUtilities.addChild(memento, "preliminaryResult", acronym);
                 }
             }
    -        
    +
             if (!run.isSolved() || (problem.isManualReview() && run.getJudgementRecord().isComputerJudgement())) {
                 XMLUtilities.addChild(memento, "solved", "false");
             } else {
                 XMLUtilities.addChild(memento, "solved", run.isSolved());
             }
             XMLUtilities.addChild(memento, "status", getStatus(run.isJudged()));
    -        
    +
     //    2
     //    
     //    1414215455.740
     //   cases = new ArrayList();
    -        
    +
             if (run.isJudged()){
                 RunTestCase[] runTestCases = run.getRunTestCases();
                 JudgementRecord judgementRecord = run.getJudgementRecord();
    -            
    +
                 for (RunTestCase runTestCaseResult : runTestCases) {
                     if (runTestCaseResult.matchesJudgement(judgementRecord)){
                         cases.add(runTestCaseResult);
                     }
                 }
             }
    -        
    -        return (RunTestCase[]) cases.toArray(new RunTestCase[cases.size()]);
    +
    +        return cases.toArray(new RunTestCase[cases.size()]);
         }
     
         /**
          * Get the set of Team accounts for the specified Contest.
    -     * 
    +     *
          * @param inContest the Contest from which Teams are to be fetched
          * @return team accounts sorted by site, team number
          */
         public Account[] getTeamAccounts(IInternalContest inContest) {
             Vector accountVector = inContest.getAccounts(ClientType.Type.TEAM);
    -        Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]);
    +        Account[] accounts = accountVector.toArray(new Account[accountVector.size()]);
             Arrays.sort(accounts, new AccountComparator());
     
             return accounts;
    @@ -1063,7 +1064,7 @@ public String createFinalizeXML(IInternalContest contest, FinalizeData data) {
     
             XMLMemento memento = XMLMemento.createWriteRoot(FINALIZE_TAG);
     
    -        addMemento (memento, contest, data); 
    +        addMemento (memento, contest, data);
     
             sb.append(toXML(memento));
     
    @@ -1076,9 +1077,9 @@ public String createFinalizeXML(IInternalContest contest, FinalizeData data) {
     
         private void addMemento(IMemento memento, IInternalContest contest, FinalizeData data) {
     
    -        
    +
             // TODO CCS validate content vs demo/sample XML
    -        
    +
     
     //        
     //        Certified by: Gunnar
    @@ -1104,9 +1105,9 @@ public XMLMemento createElement(IInternalContest contest, Group group) {
         }
     
         public void addMemento(IMemento memento, IInternalContest contest, Group group) {
    -        
    +
             // TODO CCS validate content vs demo/sample XML
    -        
    +
     
     //        
     //        5752
    @@ -1122,7 +1123,7 @@ public XMLMemento createElement(IInternalContest contest, Judgement judgement, i
             addMemento(memento, contest, judgement, sequenceNumber);
             return memento;
         }
    -    
    +
         private String getAcronym(Judgement judgement) {
     
             if (Judgement.ACRONYM_OTHER_CONTACT_STAFF.equals(judgement.getAcronym())) {
    @@ -1137,10 +1138,10 @@ private String getAcronym(Judgement judgement) {
         }
     
         public IMemento addMemento(IMemento memento, IInternalContest contest, Judgement judgement, int judgementSequence) {
    -        
    +
             // TODO CCS validate content vs demo/sample XML
    -        
    -   
    +
    +
     //        
     //        CE
     //        8
    @@ -1149,15 +1150,15 @@ public IMemento addMemento(IMemento memento, IInternalContest contest, Judgement
     
             String name = judgement.getDisplayName();
             String acronym = getAcronym(judgement);
    -        
    -        XMLUtilities.addChild(memento, "acronym", acronym); 
    -        XMLUtilities.addChild(memento, "id", judgementSequence); 
    +
    +        XMLUtilities.addChild(memento, "acronym", acronym);
    +        XMLUtilities.addChild(memento, "id", judgementSequence);
             XMLUtilities.addChild(memento, "name", name);
             return memento;
         }
     
         public XMLMemento createElement(IInternalContest contest, JudgementRecord judgementRecord) {
    -        // Judgement Record 
    +        // Judgement Record
             // 
             // CE
             // Compile Error
    diff --git a/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java b/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java
    index 8e2738040..647b76222 100644
    --- a/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java
    +++ b/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java
    @@ -1,7 +1,9 @@
    -// 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.exports.ccs;
     
    +import java.util.ArrayList;
     import java.util.Arrays;
    +import java.util.List;
     import java.util.Properties;
     import java.util.Vector;
     
    @@ -9,9 +11,10 @@
     import edu.csus.ecs.pc2.core.exception.IllegalContestState;
     import edu.csus.ecs.pc2.core.list.AccountList;
     import edu.csus.ecs.pc2.core.model.Account;
    +import edu.csus.ecs.pc2.core.model.ClientType.Type;
     import edu.csus.ecs.pc2.core.model.FinalizeData;
    +import edu.csus.ecs.pc2.core.model.Group;
     import edu.csus.ecs.pc2.core.model.IInternalContest;
    -import edu.csus.ecs.pc2.core.model.ClientType.Type;
     import edu.csus.ecs.pc2.core.scoring.DefaultScoringAlgorithm;
     import edu.csus.ecs.pc2.core.scoring.FinalsStandingsRecordComparator;
     import edu.csus.ecs.pc2.core.scoring.NewScoringAlgorithm;
    @@ -19,7 +22,7 @@
     
     /**
      * Create results.tsv file.
    - * 
    + *
      * @author pc2@ecs.csus.edu
      */
     public class ResultsFile {
    @@ -43,14 +46,14 @@ public class ResultsFile {
         private FinalizeData finalizeData = null;
     
         private FinalsStandingsRecordComparator comparator;
    -    
    +
         public void setFinalizeData(FinalizeData finalizeData) {
             this.finalizeData = finalizeData;
         }
     
         /**
          * Create CCS restuls.tsv file contents.
    -     * 
    +     *
          * @param contest
          * @return
          * @throws IllegalContestState
    @@ -58,7 +61,15 @@ public void setFinalizeData(FinalizeData finalizeData) {
         public String[] createTSVFileLines(IInternalContest contest) {
             return createTSVFileLines(contest, DEFAULT_RESULT_FIELD_NAME);
         }
    -    
    +
    +    public String [] createTSVFileLines(IInternalContest contest, Group group) {
    +        return createTSVFileLines(contest, group, DEFAULT_RESULT_FIELD_NAME);
    +    }
    +
    +    private String [] createTSVFileLines(IInternalContest contest, String resultFileTitleFieldName) {
    +        return createTSVFileLines(contest, null, resultFileTitleFieldName);
    +    }
    +
         /**
          * Input is a sorted ranking list.  What is the median number of problems solved.
          * copied from DefaultScoringAlgorithm, maybe it should be a common location?
    @@ -90,17 +101,17 @@ private int getMedian(StandingsRecord[] srArray) {
     
         /**
          * Create CCS restuls.tsv file contents.
    -     * 
    +     *
          * @param contest
    -     * @param resultFileTitleFieldName override title anem {@value #DEFAULT_RESULT_FIELD_NAME}. 
    +     * @param resultFileTitleFieldName override title anem {@value #DEFAULT_RESULT_FIELD_NAME}.
          * @return
          */
    -    public String[] createTSVFileLines(IInternalContest contest, String resultFileTitleFieldName)  {
    +    public String[] createTSVFileLines(IInternalContest contest, Group group, String resultFileTitleFieldName)  {
     
             Vector lines = new Vector();
     
             finalizeData = contest.getFinalizeData();
    -        
    +
             NewScoringAlgorithm scoringAlgorithm = new NewScoringAlgorithm();
             scoringAlgorithm.setContest(contest);
     
    @@ -115,25 +126,30 @@ public String[] createTSVFileLines(IInternalContest contest, String resultFileTi
             // return ranked teams
             StandingsRecord[] standingsRecords = null;
             try {
    -            standingsRecords = scoringAlgorithm.getStandingsRecords(contest, properties);
    +            List groupList = null;
    +            if(group != null) {
    +                groupList = new ArrayList();
    +                groupList.add(group);
    +            }
    +            standingsRecords = scoringAlgorithm.getStandingsRecords(contest, null,  groupList, properties, false, null);
             } catch (Exception e) {
                 throw new RuntimeException("Unable to generate standings ", e.getCause());
             }
    -        
    +
             int median = getMedian(standingsRecords);
    -        
    +
             if (finalizeData == null) {
                 String [] badbad = {"Contest not finalized cannot create awards"};
    -            return badbad;  
    +            return badbad;
             }
    -        
    +
             // TODO finalizeData really needs a B instead of getBronzeRank
             int lastMedalRank = finalizeData.getBronzeRank();
             int lastSolvedNum = 0;
             int rankNumber = 0;
             // resort standingsRecord based on lastMedalRank and median
             Vector accountVector = contest.getAccounts(Type.TEAM);
    -        Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]);
    +        Account[] accounts = accountVector.toArray(new Account[accountVector.size()]);
             AccountList accountList = new AccountList();
             for (Account account : accounts) {
                 accountList.add(account);
    @@ -177,23 +193,23 @@ public String[] createTSVFileLines(IInternalContest contest, String resultFileTi
                 }
                 lines.addElement(reservationId + TAB //
                         + rank + TAB //
    -                    + award + TAB  // 
    +                    + award + TAB  //
                         + record.getNumberSolved() //
                         + TAB + record.getPenaltyPoints() + TAB //
                         + record.getLastSolved());
             }
     
    -        return (String[]) lines.toArray(new String[lines.size()]);
    +        return lines.toArray(new String[lines.size()]);
         }
     
         /**
          * Determine and return award medal color.
    -     * 
    +     *
          * 
    -     * Award is a string with value "gold", "silver", "bronze", "ranked" 
    +     * Award is a string with value "gold", "silver", "bronze", "ranked"
          * or "honorable" as appropriate.
          * 
    - * + * * @param rankNumber * @param finalizeData * @return @@ -228,7 +244,7 @@ protected Properties getScoringProperties(IInternalContest contest) { /** * Fill in with default properties if not using them. */ - String[] keys = (String[]) defProperties.keySet().toArray(new String[defProperties.keySet().size()]); + String[] keys = defProperties.keySet().toArray(new String[defProperties.keySet().size()]); for (String key : keys) { if (!properties.containsKey(key)) { properties.put(key, defProperties.get(key)); @@ -237,18 +253,18 @@ protected Properties getScoringProperties(IInternalContest contest) { return properties; } - + public String[] createTSVFileLinesTwo(IInternalContest contest) throws Exception { - finalizeData = contest.getFinalizeData(); + finalizeData = contest.getFinalizeData(); DefaultScoringAlgorithm scoringAlgorithm = new DefaultScoringAlgorithm(); String xmlString = scoringAlgorithm.getStandings(contest, new Properties(), null); String xsltFileName = "results.tsv.xsl"; - + return XMLUtilities.transformToArray(xmlString, xsltFileName); } - + } diff --git a/src/edu/csus/ecs/pc2/exports/ccs/ScoreboardFile.java b/src/edu/csus/ecs/pc2/exports/ccs/ScoreboardFile.java index 0e9215331..a5e0b0153 100644 --- a/src/edu/csus/ecs/pc2/exports/ccs/ScoreboardFile.java +++ b/src/edu/csus/ecs/pc2/exports/ccs/ScoreboardFile.java @@ -1,11 +1,14 @@ -// 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.exports.ccs; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import java.util.Vector; import edu.csus.ecs.pc2.core.exception.IllegalContestState; import edu.csus.ecs.pc2.core.model.Account; +import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Problem; import edu.csus.ecs.pc2.core.scoring.DefaultScoringAlgorithm; @@ -16,7 +19,7 @@ /** * Create scoreboard.tsv file. - * + * * @author pc2@ecs.csus.edu * @version $Id: ScoreboardFile.java 180 2011-04-11 00:36:50Z laned $ */ @@ -28,13 +31,16 @@ public class ScoreboardFile { /** * Create CCS scoreboard.tsv file contents. - * + * * @param contest * @return * @throws IllegalContestState */ public String[] createTSVFileLines(IInternalContest contest) throws IllegalContestState { + return(createTSVFileLines(contest, null)); + } + public String[] createTSVFileLines(IInternalContest contest, Group group) throws IllegalContestState { Vector lines = new Vector(); NewScoringAlgorithm scoringAlgorithm = new NewScoringAlgorithm(); @@ -42,6 +48,14 @@ public String[] createTSVFileLines(IInternalContest contest) throws IllegalConte Properties properties = getScoringProperties(contest); + List groupList = null; + + if(group != null) { + groupList = new ArrayList(); + groupList.add(group); + } + + // Field Description Example Type // 1 Label scoreboard fixed string (always same value) // 2 Version number 1 integer @@ -49,7 +63,7 @@ public String[] createTSVFileLines(IInternalContest contest) throws IllegalConte lines.addElement("scoreboard" + TAB + "1"); // return ranked teams - StandingsRecord[] standingsRecords = scoringAlgorithm.getStandingsRecords(contest, properties); + StandingsRecord[] standingsRecords = scoringAlgorithm.getStandingsRecords(contest, null, groupList, properties, false, null); for (StandingsRecord record : standingsRecords) { @@ -105,7 +119,7 @@ public String[] createTSVFileLines(IInternalContest contest) throws IllegalConte lines.addElement(line); } - return (String[]) lines.toArray(new String[lines.size()]); + return lines.toArray(new String[lines.size()]); } @@ -121,7 +135,7 @@ protected Properties getScoringProperties(IInternalContest contest) { /** * Fill in with default properties if not using them. */ - String[] keys = (String[]) defProperties.keySet().toArray(new String[defProperties.keySet().size()]); + String[] keys = defProperties.keySet().toArray(new String[defProperties.keySet().size()]); for (String key : keys) { if (!properties.containsKey(key)) { properties.put(key, defProperties.get(key)); diff --git a/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON.java b/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON.java index 8ca766e0a..8bc211589 100644 --- a/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON.java +++ b/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON.java @@ -1,4 +1,4 @@ -// 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.exports.ccs; import java.util.ArrayList; @@ -19,7 +19,7 @@ /** * Standings information in JSON format. - * + * * @author pc2@ecs.csus.edu * @version $Id: StandingsJSON.java 341 2013-06-21 10:53:25Z laned $ */ @@ -35,7 +35,7 @@ public String createJSON(IInternalContest contest) throws IllegalContestState { NewScoringAlgorithm scoringAlgorithm = new NewScoringAlgorithm(); scoringAlgorithm.setContest(contest); - + ContestInformation info = contest.getContestInformation(); Properties properties = new Properties(); if (info != null) { @@ -56,8 +56,10 @@ public String createJSON(IInternalContest contest) throws IllegalContestState { Account account = contest.getAccount(clientId); String universityName = account.getDisplayName(); String groupName = ""; - if (account.getGroupId() != null) { - groupName = contest.getGroup(account.getGroupId()).toString(); + // TODO: Probably should create a JSON for each group ID for this team. This would + // involve changing the Report interface to make successive calls supplying a group ID -- JB + if (account.getPrimaryGroupId() != null) { + groupName = contest.getGroup(account.getPrimaryGroupId()).toString(); } // {"id":"","name":"","group":"" @@ -122,8 +124,8 @@ private String pair(String name, long value) { /** * return a letter for a number. - * - * + * + * * @return 0 = A, 1 = B, etc. */ public static String getProblemLetter(int id) { diff --git a/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON2016.java b/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON2016.java index 7f1e1daf6..0752db28e 100644 --- a/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON2016.java +++ b/src/edu/csus/ecs/pc2/exports/ccs/StandingsJSON2016.java @@ -1,6 +1,7 @@ -// 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.exports.ccs; +import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -9,6 +10,7 @@ import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ContestInformation; +import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Problem; import edu.csus.ecs.pc2.core.scoring.NewScoringAlgorithm; @@ -19,7 +21,7 @@ /** * Standings information in CLI 2016 JSON format. - * + * * @author pc2@ecs.csus.edu * @version $Id: StandingsJSON.java 341 2013-06-21 10:53:25Z laned $ */ @@ -29,7 +31,7 @@ public class StandingsJSON2016 { /** * Returns a JSON string describing the current contest standings in the format defined by the 2016 CLI JSON Scoreboard. * The format follows this JSON example, from the CLI Wiki JSON Scoreboard page: - * + * *
          *  [ {
          *     "rank":1,"team":42,"score":{"num_solved":3,"total_time":340},
    @@ -44,13 +46,17 @@ public class StandingsJSON2016 {
          *    ...
          *  ]
          * 
    - * + * * @param contest - the current contest * @param controller - the current contest controller * @return a JSON string giving contest standings in 2016 format * @throws IllegalContestState */ public String createJSON(IInternalContest contest, IInternalController controller) throws IllegalContestState { + return(createJSON(contest, controller, null)); + } + + public String createJSON(IInternalContest contest, IInternalController controller, Group group) throws IllegalContestState { if (contest == null) { return "[]"; @@ -58,16 +64,24 @@ public String createJSON(IInternalContest contest, IInternalController controlle NewScoringAlgorithm scoringAlgorithm = new NewScoringAlgorithm(); scoringAlgorithm.setContest(contest); - + ContestInformation info = contest.getContestInformation(); Properties properties = new Properties(); + if (info != null) { if (info.getScoringProperties() != null) { properties = info.getScoringProperties(); } } - - StandingsRecord[] standingsRecords = scoringAlgorithm.getStandingsRecords(contest, properties); + + List groupList = null; + + if(group != null) { + groupList = new ArrayList(); + groupList.add(group); + } + + StandingsRecord[] standingsRecords = scoringAlgorithm.getStandingsRecords(contest, null, groupList, properties, false, null); RunStatistics runStatistics = new RunStatistics(contest); @@ -75,7 +89,7 @@ public String createJSON(IInternalContest contest, IInternalController controlle int rank = 1; for (StandingsRecord sr : standingsRecords) { - + //start a new JSON element for the current standings record //if it's not the first rank being output, add a comma separator if (rank!=1) { @@ -83,7 +97,7 @@ public String createJSON(IInternalContest contest, IInternalController controlle } rank++ ; buffer.append('{'); - + ClientId clientId = sr.getClientId(); Account account = contest.getAccount(clientId); int teamNum = account.getClientId().getClientNumber(); @@ -95,12 +109,12 @@ public String createJSON(IInternalContest contest, IInternalController controlle long numSolved = sr.getNumberSolved(); long totalTime = sr.getPenaltyPoints(); buffer.append( "\"score\":{" + pair("num_solved",numSolved) + "," + pair("total_time",totalTime) + "},"); - + //add the "problems" array to the buffer buffer.append("\"problems\":["); SummaryRow row = sr.getSummaryRow(); - + //for each problem for (int i = 0; i < contest.getProblems().length; i++) { int problemIndex = i + 1; @@ -122,14 +136,14 @@ public String createJSON(IInternalContest contest, IInternalController controlle int numSubmitted = summaryInfo.getNumberSubmitted(); int numPending = summaryInfo.getPendingRunCount(); int numJudged = summaryInfo.getJudgedRunCount(); - + // //debug: -// System.out.println ("StandingsJSON2016: " -// + "Team: "+ teamNum + " problem: '" + letter + "'" +// System.out.println ("StandingsJSON2016: " +// + "Team: "+ teamNum + " problem: '" + letter + "'" // + " numSubmitted: " + numSubmitted -// + " numPending: " + numPending +// + " numPending: " + numPending // + " numJudged: " + numJudged); - + //verify data makes sense if ((numPending+numJudged) != numSubmitted) { System.err.println ("StandingsJSON2016: mismatch: numPendingRuns+numJudgedRuns!=numSubmittedRuns (" @@ -140,17 +154,17 @@ public String createJSON(IInternalContest contest, IInternalController controlle // add the number of judging-completed runs to the buffer buffer.append(pair("num_judged", numJudged) + ","); - + //add the number of pending runs to the buffer buffer.append(pair("num_pending",numPending) + ","); - + //add the field indicating whether the problem has been solved String isSolved = "false"; if (summaryInfo.isSolved()) { isSolved = "true"; } buffer.append("\"solved\":" + isSolved); - + //if the problem was solved, add the fields showing solution time and whether the solution was the first-to-solve if (summaryInfo.isSolved()) { long solutionTime = summaryInfo.getSolutionTime(); @@ -162,7 +176,7 @@ public String createJSON(IInternalContest contest, IInternalController controlle } buffer.append("," + "\"first_to_solve\":" + fts); } - + //close the problem description buffer.append("}"); @@ -172,12 +186,12 @@ public String createJSON(IInternalContest contest, IInternalController controlle } //close the "problems" array buffer.append("]"); - + //close the entry for the current StandingsRecord (team) buffer.append('}'); } - + //return the collected standings as elements of a JSON array return "[" + buffer.toString() + "]"; } @@ -199,8 +213,8 @@ private String pair(String name, long value) { /** * return a letter for a number. - * - * + * + * * @return 0 = A, 1 = B, etc. */ public static String getProblemLetter(int id) { diff --git a/src/edu/csus/ecs/pc2/exports/ccs/Teamdata.java b/src/edu/csus/ecs/pc2/exports/ccs/Teamdata.java index fa4352596..2b28d7892 100644 --- a/src/edu/csus/ecs/pc2/exports/ccs/Teamdata.java +++ b/src/edu/csus/ecs/pc2/exports/ccs/Teamdata.java @@ -1,4 +1,4 @@ -// 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.exports.ccs; import java.util.Arrays; @@ -12,9 +12,9 @@ /** * Create file for team.tsv. - * + * * Create lines that can be printed to team.tsv file. - * + * * @author pc2@ecs.csus.edu * @version $Id: Userdata.java 231 2011-09-03 20:20:59Z laned $ */ @@ -26,7 +26,7 @@ public class Teamdata { // /** // * Create TeamData file for all accounts -// * +// * // * @param contest // * @return // */ @@ -37,7 +37,7 @@ public class Teamdata { // // /** // * Get all teams, judges and non-admins/board accounts. -// * +// * // * @param contest // * @return // */ @@ -66,10 +66,10 @@ public class Teamdata { // // return getTeamData(contest, (Account[]) list.toArray(new Account[list.size()])); // } - + public String[] getTeamData(IInternalContest contest) { Vector accountlist = contest.getAccounts(Type.TEAM); - Account [] accounts = (Account[]) accountlist.toArray(new Account[accountlist.size()]); + Account [] accounts = accountlist.toArray(new Account[accountlist.size()]); return getTeamData(contest, accounts); } @@ -117,15 +117,17 @@ public String teamDataLine(IInternalContest contest, Account account) { // 7 Country USA string ISO 3166-1 alpha-3 int groupId = 0; - if (account.getGroupId() != null) { - groupId = contest.getGroup(account.getGroupId()).getGroupId(); + // Since this is a legacy format, the user of this data is not expecting more than one group, + // so we use the CMS primary group. + if (account.getPrimaryGroupId() != null) { + groupId = contest.getGroup(account.getPrimaryGroupId()).getGroupId(); } - + String longSchoolName = setString(account.getLongSchoolName(), "undefined"); String countryCode = setString(account.getCountryCode(), "XXX"); String shortSchoolName = setString(account.getShortSchoolName(),"undefined"); - + return clientId.getClientNumber() + TAB + // account.getExternalId() + TAB + // groupId + TAB + // @@ -136,13 +138,13 @@ public String teamDataLine(IInternalContest contest, Account account) { } private String setString(String string, String defaultString) { - + if (string == null || string.trim().length() == 0){ return defaultString; } else { return string; } - + // return string; } } diff --git a/src/edu/csus/ecs/pc2/exports/ccs/TeamsJSON.java b/src/edu/csus/ecs/pc2/exports/ccs/TeamsJSON.java deleted file mode 100644 index 1a104d924..000000000 --- a/src/edu/csus/ecs/pc2/exports/ccs/TeamsJSON.java +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. -package edu.csus.ecs.pc2.exports.ccs; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Vector; - -import edu.csus.ecs.pc2.core.exception.IllegalContestState; -import edu.csus.ecs.pc2.core.model.Account; -import edu.csus.ecs.pc2.core.model.ElementId; -import edu.csus.ecs.pc2.core.model.Group; -import edu.csus.ecs.pc2.core.model.IInternalContest; -import edu.csus.ecs.pc2.core.model.ClientType.Type; - -/** - * Team information in CLI 2016 JSON format. - * - * @author pc2@ecs.csus.edu - */ - -public class TeamsJSON { - - /** - * Returns a JSON string listing the current contest teams in the format defined by the 2016 CLI JSON Scoreboard. - * - * @param contest - the current contest - * @return a JSON string giving contest teams in 2016 format - * @throws IllegalContestState - */ - public String createJSON(IInternalContest contest) throws IllegalContestState { - - if (contest == null) { - return "[]"; - } - - Vector accountlist = contest.getAccounts(Type.TEAM); - if (accountlist.size() == 0) { - return "[]"; - } - Account[] accounts = (Account[]) accountlist.toArray(new Account[accountlist.size()]); - - Group[] groups = contest.getGroups(); - final Map groupMap = new HashMap(); - for (Group group : groups) { - groupMap.put(group.getElementId(), group.getDisplayName()); - } - StringBuffer buffer = new StringBuffer(); - - int rowCount = 1; - for (Account account : accounts) { - /* add comma between rows */ - if (rowCount != 1) { - buffer.append(','); - } - buffer.append('{'); - - //build the team data entry for the current team, using the format defined in the 2016 JSON Scoreboard spec which is as follows: - /* - * [{"id":42,"name":"Shanghai Tigers","nationality":"CHN","affiliation":"Shanghai Jiao Tong University","group":"Asia"}, - * {"id":11,"name":"CMU1","nationality":"USA","affiliation":"Carnegie Mellon University","group":"North America"}, ... ] - */ - //get team number - int teamNum = account.getClientId().getClientNumber(); - - //get team name, force to "null" if undefined - String teamName = account.getTeamName(); - if (teamName==null || teamName.trim().equals("")) { - teamName = "null"; - } - //get country code, force to "null" if undefined - String countryCode = account.getCountryCode(); - if (countryCode==null || countryCode.trim().equals("")) { - countryCode = "null"; - } - //get "affiliation" (school); force to "null" if undefined - String schoolName = account.getLongSchoolName(); - if (schoolName==null || schoolName.trim().equals("")) { - schoolName = "null"; - } - //get group name, forcing to "null" if undefined - String groupName = "null"; - if (account.getGroupId() != null && groupMap.containsKey(account.getGroupId())) { - groupName = groupMap.get(account.getGroupId()); - } - //add the above team data to the output buffer - buffer.append(pair("id", teamNum) + "," + pair("name", teamName) + "," + pair("nationality", countryCode) + "," - + pair("affliliation", schoolName) + "," + pair("group", groupName)); - - // close the entry for the current team entry - buffer.append('}'); - rowCount++; - } - - // return the collected standings as elements of a JSON array - return "[" + buffer.toString() + "]"; - } - - public static String join(String delimit, List list) { - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < list.size(); i++) { - buffer.append(list.get(i)); - if (i < list.size() - 1) { - buffer.append(delimit); - } - } - return buffer.toString(); - } - - /* - * these should be a utility class - */ - private String pair(String name, long value) { - return "\"" + name + "\":" + value; - } - - private String pair(String name, String value) { - return "\"" + name + "\":\"" + value + "\""; - } -} diff --git a/src/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoader.java b/src/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoader.java index 3edfe320a..2443f7758 100644 --- a/src/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoader.java +++ b/src/edu/csus/ecs/pc2/imports/ccs/ICPCTSVLoader.java @@ -1,4 +1,4 @@ -// 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.imports.ccs; import java.io.FileNotFoundException; @@ -11,16 +11,15 @@ import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; -import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.security.PermissionList; import edu.csus.ecs.pc2.core.util.TabSeparatedValueParser; /** * ICPC CCS TSV File Loader. - * + * * Loads group and teams.tsv files into Group and Account lists. - * + * * @author pc2@ecs.csus.edu */ public final class ICPCTSVLoader { @@ -49,7 +48,7 @@ private ICPCTSVLoader() { // 7 Country USA string ISO 3166-1 alpha-3 private static final int TEAM_TSV_FIELDS = 7; - // plus + // plus // 8 institute id "INST-" + integer private static final int TEAM2_TSV_FIELDS = 8; @@ -79,10 +78,10 @@ private ICPCTSVLoader() { private static HashMap institutionsMap = new HashMap(); /** - * Load teams.tsv, use {@link #loadGroups(String)} first. - * + * Load teams.tsv or teams2.tsv, use {@link #loadGroups(String)} first. + * * {@link #setGroups(Group[])} must be invoked before using this method so groupIds are assigned. - * + * * @param filename * @return * @throws Exception @@ -94,7 +93,7 @@ public static Account[] loadAccounts(String filename) throws Exception { if (lines.length == 0) { throw new FileNotFoundException(filename); } - + int i = 0; String firstLine = lines[i]; @@ -109,7 +108,7 @@ public static Account[] loadAccounts(String filename) throws Exception { // TODO CCS check for 'teams' when tsv file contains that value. // validate first line // String[] fields = firstLine.split("\t"); - + // if (!fields[0].trim().equals("teams")) { // throw new InvalidValueException("Expecting 'teams' got '" + fields[0] + "' in " + filename); // } @@ -140,11 +139,11 @@ public static Account[] loadAccounts(String filename) throws Exception { } private static Account accountFromFields(String[] fields, int accountCount, String originalLine) throws InvaildNumberFields, InvalidValueException { - + if (!(fields.length == TEAM_TSV_FIELDS || fields.length == TEAM2_TSV_FIELDS)) { throw new InvaildNumberFields("Expected " + TEAM_TSV_FIELDS + " (or "+ TEAM2_TSV_FIELDS + ") fields, found " + fields.length + " invalid team.tsv line: " + originalLine); } - + int fieldnum = 0; String numberStr = fields[fieldnum++]; String reservationIdStr = fields[fieldnum++]; @@ -167,14 +166,14 @@ private static Account accountFromFields(String[] fields, int accountCount, Stri // 1 Team number 22 integer // 2 Reservation ID 24314 integer - // 3 Group ID 4 integer + // 3 Group IDs 3023,3025 CSV CMS groups // 4 Team name Hoos string // 5 Institution name University of Virginia string // 6 Institution short name U Virginia string // 7 Country USA string ISO 3166-1 alpha-3 int number = 0; - + if (numberStr != null && NULL_STRING.equalsIgnoreCase(numberStr.trim())){ /** * If team number field is 'null' then assign a team number. @@ -190,13 +189,6 @@ private static Account accountFromFields(String[] fields, int accountCount, Stri } } - int groupNumber = 0; - try { - groupNumber = Integer.parseInt(groupIDStr); - } catch (NumberFormatException e) { - throw new InvalidValueException("Expecting group number got '" + numberStr + "' on line " + originalLine, e); - } - ClientId clientId = new ClientId(siteNumber, ClientType.Type.TEAM, number); Account account = new Account(clientId, getJoePassword(clientId), siteNumber); @@ -216,37 +208,38 @@ private static Account accountFromFields(String[] fields, int accountCount, Stri account.setInstitutionShortName(institutionName); } } - - if (groupNumber != 0){ - /** - * Only lookup group number if not zero. - */ - ElementId groupId = getGroupForNumber(groupNumber); - - if (groupId != null) { - account.setGroupId(groupId); - } else { - throw new InvalidValueException("Unknown group number '" + groupNumber + "' on line " + originalLine); - } - } - return account; - } + String [] cmsGroups = groupIDStr.split(","); + int groupNumber = 0; + boolean primaryGroup = true; - private static ElementId getGroupForNumber(int groupId) { - - for (Group group : groups) { - if (group.getGroupId() == groupId) { - return group.getElementId(); + try { + for(String cmsGroup : cmsGroups) { + groupNumber = Integer.parseInt(cmsGroup); + if (groupNumber != 0){ + /** + * Only lookup group number if not zero. + */ + Group group = findGroupById(groups, groupNumber); + + if (group != null) { + account.addGroupId(group.getElementId(), primaryGroup); + primaryGroup = false; + } else { + throw new InvalidValueException("Unknown group number '" + groupNumber + "' on line " + originalLine); + } + } } + } catch (NumberFormatException e) { + throw new InvalidValueException("Expecting group number got '" + numberStr + "' on line " + originalLine, e); } - return null; + return account; } /** * return joe password for clientid. - * + * * @param clientId * @return */ @@ -257,21 +250,21 @@ private static String getJoePassword(ClientId clientId) { public static void setGroups(Group[] groups) { ICPCTSVLoader.groups = groups; } - + /** * Merge/Add group data from filename and authoritativeGroups. - * + * * @param filename name of groups.tsv file. - * @return list of groups + * @return list of groups * @throws Exception */ public static Group[] loadGroups(String filename) throws Exception { return loadGroups(filename, new Group[0]); } - + /** * Merge/Add group data from filename and authoritativeGroups. - * + * * @param filename name of groups.tsv file. * @param authoritativeGroups an array of groups (from contest/model) * @return unique groups from authoritativeGroups and filename's group list. @@ -279,9 +272,9 @@ public static Group[] loadGroups(String filename) throws Exception { */ public static Group[] loadGroups(String filename, Group[] authoritativeGroups) throws Exception { List groupList = new ArrayList(); - + String[] lines = CCSListUtilities.filterOutCommentLines(Utilities.loadFile(filename)); - + if (lines.length == 0) { throw new FileNotFoundException(filename); } @@ -325,18 +318,18 @@ public static Group[] loadGroups(String filename, Group[] authoritativeGroups) t } catch (NumberFormatException e) { throw new InvalidValueException("Expecting group number got '" + numberStr + "' on line " + lines[i], e); } - + Group group = findGroupById(authoritativeGroups, number); if (group == null) { group = new Group(groupName); } - + group.setDisplayName(groupName); group.setGroupId(number); - + groupList.add(group); } - + /** * Add groups from authoritativeGroups if missing from groupList. */ @@ -346,15 +339,15 @@ public static Group[] loadGroups(String filename, Group[] authoritativeGroups) t break; } } - - groups = (Group[]) groupList.toArray(new Group[groupList.size()]); - + + groups = groupList.toArray(new Group[groupList.size()]); + return groups; } /** * Search and return group if found. - * + * * @param authoritativeGroups list of groups * @param groupCMSId CMS group id * @return null if not found, else the group @@ -395,20 +388,20 @@ public static void loadInstitutions(String filename) throws Exception { institutionsMap.put(icpcId, fields); } } - + /** * Returns the corresponding institution information for the supplied institution code. * If the code is not recognized, a null array is returned. * Due to the non-specificity of the format of an institution code, we have to check if * it has the INST-U- or INST- prefixes, if so, remove them and try again. * The map must be filled in via loadInstitutions() first or it will always return false. - * + * * @param instCode (number, INST-U-number or INST-number) * @return array of 3 strings: [0]=code, [1]=formal name [2]=short name, or null if not found */ public static String[] getInstitutionNames(String instCode) { String[] names = null; - + if(institutionsMap.containsKey(instCode)) { names = institutionsMap.get(instCode); } else { @@ -425,7 +418,7 @@ public static String[] getInstitutionNames(String instCode) { } return(names); } - + public static HashMap loadPasswordsFromAccountsTSV(String filename) throws Exception { String[] lines = CCSListUtilities.filterOutCommentLines(Utilities.loadFile(filename)); @@ -445,5 +438,5 @@ public static HashMap loadPasswordsFromAccountsTSV(String filen return passwordMap; } - + } diff --git a/src/edu/csus/ecs/pc2/services/core/EventFeedJSON.java b/src/edu/csus/ecs/pc2/services/core/EventFeedJSON.java index e1069471a..39946d8d7 100644 --- a/src/edu/csus/ecs/pc2/services/core/EventFeedJSON.java +++ b/src/edu/csus/ecs/pc2/services/core/EventFeedJSON.java @@ -1,7 +1,8 @@ -// 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.services.core; import java.util.Arrays; +import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Vector; @@ -21,6 +22,7 @@ import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; import edu.csus.ecs.pc2.core.model.ElementId; +import edu.csus.ecs.pc2.core.model.Filter; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Judgement; @@ -35,13 +37,13 @@ /** * Event feed information in the CLICS JSON format. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ // TODO for all sections pass in Key rather than hard coded inside method public class EventFeedJSON extends JSONUtilities { /** - * + * */ public EventFeedJSON(IInternalContest contest) { super(); @@ -54,29 +56,31 @@ public EventFeedJSON(IInternalContest contest) { /** * Event Id Sequence. - * + * * @see #nextEventId() */ protected long eventIdSequence = 0; /** * Start event id. - * + * * /event-feed?type= */ private String startEventId = null; /** * List of events to output. - * + * */ private String eventTypeList = null; private JSONTool jsonTool; - private Vector ignoreGroup = new Vector(); + private Filter filter = null; - private Vector ignoreTeam = new Vector(); + private HashSet ignoreGroup = new HashSet(); + + private HashSet ignoreTeam = new HashSet(); public String getContestJSON(IInternalContest contest) { @@ -103,7 +107,7 @@ public String getStateJSON(IInternalContest contest) { } /** * List of judgements. - * + * */ public String getJudgementTypeJSON(IInternalContest contest) { @@ -124,7 +128,7 @@ String getJudgementTypeJSON(IInternalContest contest, Judgement judgement) { /** * Get all languages JSON. - * + * * @param contest * @return */ @@ -147,7 +151,7 @@ public String getLanguageJSON(IInternalContest contest) { /** * get JSON for a language. - * + * * @param contest * @param language * @param languageNumber @@ -165,7 +169,7 @@ public String getProblemJSON(IInternalContest contest) { Problem[] problems = contest.getProblems(); int id = 1; for (Problem problem : problems) { - if (problem.isActive()) { + if (problem.isActive() && (filter == null || filter.matches(problem))) { appendJSONEvent(stringBuilder, PROBLEM_KEY, ++eventIdSequence, EventFeedOperation.CREATE, getProblemJSON(contest, problem, id)); stringBuilder.append(NL); id++; @@ -185,9 +189,13 @@ public String getGroupJSON(IInternalContest contest) { Group[] groups = contest.getGroups(); Arrays.sort(groups, new GroupComparator()); + + HashSet usedGroups = getGroupsUsed(contest); for (Group group : groups) { - if (group.isDisplayOnScoreboard()) { + // Put this group in the event feed if teams are not members of any group or one of the teams + // that matches the (possibly in-effect) filters used this group. + if (usedGroups == null || usedGroups.contains(group.getElementId())) { appendJSONEvent(stringBuilder, GROUPS_KEY, ++eventIdSequence, EventFeedOperation.CREATE, getGroupJSON(contest, group)); stringBuilder.append(NL); } else { @@ -198,6 +206,36 @@ public String getGroupJSON(IInternalContest contest) { return stringBuilder.toString(); } + /** + * Returns a hashset that includes all groups that any (possibly filtered) teams are members of. + * We need this so we include all "used" groups in the event feed. + * + * @param contest + * @return Set of Group elementId's found in any matching or null if none found + */ + private HashSet getGroupsUsed(IInternalContest contest) { + + Account[] accounts = getTeamAccounts(contest); + + HashSet usedGroups = new HashSet(); + + for (Account account : accounts) { + + if (account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD) && isDisplayAccountGroupOnScoreboard(account) && (filter == null || filter.matches(account))) { + HashSet groups = account.getGroupIds(); + if(groups != null) { + for(ElementId groupElementId : groups) { + usedGroups.add(groupElementId); + } + } + } + } + if(usedGroups.size() == 0) { + usedGroups = null; + } + return usedGroups; + } + protected String getGroupJSON(IInternalContest contest, Group group) { return jsonTool.convertToJSON(group).toString(); @@ -212,7 +250,7 @@ public String getOrganizationJSON(IInternalContest contest) { Hashtable organizations = new Hashtable(); for (Account account : accounts) { - if (account.getClientId().getClientType().equals(ClientType.Type.TEAM) && !account.getInstitutionCode().equals("undefined")) { + if (account.getClientId().getClientType().equals(ClientType.Type.TEAM) && (filter == null || filter.matches(account)) && !account.getInstitutionCode().equals("undefined")) { if (!organizations.containsKey(account.getInstitutionCode())) { organizations.put(account.getInstitutionCode(), account); appendJSONEvent(stringBuilder, ORGANIZATION_KEY, ++eventIdSequence, EventFeedOperation.CREATE, jsonTool.convertOrganizationsToJSON(account).toString()); @@ -225,14 +263,14 @@ public String getOrganizationJSON(IInternalContest contest) { } /** - * Get all sites' teams. - * + * Get all sites' teams in sorted order. + * * @param contest - * @return + * @return array of sorted teams */ public Account[] getTeamAccounts(IInternalContest inContest) { Vector accountVector = inContest.getAccounts(ClientType.Type.TEAM); - Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + Account[] accounts = accountVector.toArray(new Account[accountVector.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; @@ -243,11 +281,10 @@ public String getTeamJSON(IInternalContest contest) { StringBuilder stringBuilder = new StringBuilder(); Account[] accounts = getTeamAccounts(contest); - Arrays.sort(accounts, new AccountComparator()); for (Account account : accounts) { - if (account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD) && !ignoreGroup.contains(account.getGroupId())) { + if (account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD) && isDisplayAccountGroupOnScoreboard(account) && (filter == null || filter.matches(account))) { appendJSONEvent(stringBuilder, TEAM_KEY, ++eventIdSequence, EventFeedOperation.CREATE, getTeamJSON(contest, account)); stringBuilder.append(NL); } else { @@ -262,16 +299,39 @@ public String getTeamJSON(IInternalContest contest, Account account) { return jsonTool.convertToJSON(account).toString(); } + /** + * Determine if the supplied account is to be shown on the scoreboard based on the groups it belongs to + * + * @param account to check if it has a group that can be displayed + * @return true if the account should be displayed, false otherwise + */ + private boolean isDisplayAccountGroupOnScoreboard(Account account) + { + HashSet groups = account.getGroupIds(); + boolean canDisplay = false; + if(groups != null) { + for(ElementId groupElementId : groups) { + if(!ignoreGroup.contains(groupElementId)) { + canDisplay = true; + break; + } + } + } else { + // If no groups for account, then it's ok to display + canDisplay = true; + } + return(canDisplay); + } + /** * Get team member info. - * + * */ public String getTeamMemberJSON(IInternalContest contest) { StringBuilder stringBuilder = new StringBuilder(); Account[] accounts = getTeamAccounts(contest); - Arrays.sort(accounts, new AccountComparator()); for (Account account : accounts) { String[] names = account.getMemberNames(); @@ -293,7 +353,7 @@ protected String getTeamMemberJSON(IInternalContest contest, Account account, St /** * Get run submission. - * + * * @param contest * @return */ @@ -320,7 +380,7 @@ public String getSubmissionJSON(IInternalContest contest, Run run, HttpServletRe /** * List of all runs' judgements.. - * + * */ public String getJudgementJSON(IInternalContest contest) { @@ -347,7 +407,7 @@ private String getJudgementJSON(IInternalContest contest, Run run) { /** * Return test cases. - * + * * @param contest */ public String getRunJSON(IInternalContest contest) { @@ -379,7 +439,7 @@ private String getRunJSON(IInternalContest contest, RunTestCase[] runTestCases, /** * Clarification Answer. - * + * * @param contest * @return */ @@ -428,13 +488,13 @@ public String createJSON(IInternalContest contest, EventFeedFilter filter, HttpS // SOMEDAY in Java 8 return String.join(NL, list) + NL; - String[] sa = (String[]) list.toArray(new String[list.size()]); + String[] sa = list.toArray(new String[list.size()]); return StringUtilities.join(NL, sa) + NL; } /** * Returns a JSON string listing the current contest event feed - * + * * @param contest * - the current contest * @return a JSON string giving event feed in JSON @@ -529,9 +589,9 @@ public String createJSON(IInternalContest contest, HttpServletRequest servletReq /** * Appends named event types onto a buffer. - * + * * valid events are: awards, clarifications, contests, groups, judgement-types, judgements, languages, organizations, problems, runs, submissions, team-members, teams - * + * * @param contest * @param buffer * @param inEventTypeList @@ -612,7 +672,7 @@ public long nextEventIdSequence() { /** * get event id. - * + * * @param sequenceNumber * ascending number * @return event Id @@ -651,4 +711,12 @@ public long getEventIdSequence() { public void setEventIdSequence(long eventIdSequence) { this.eventIdSequence = eventIdSequence; } + + public Filter getFilter() { + return filter; + } + + public void setFilter(Filter filter) { + this.filter = filter; + } } diff --git a/src/edu/csus/ecs/pc2/services/core/TeamJSON.java b/src/edu/csus/ecs/pc2/services/core/TeamJSON.java index 71f3ee223..17a49d100 100644 --- a/src/edu/csus/ecs/pc2/services/core/TeamJSON.java +++ b/src/edu/csus/ecs/pc2/services/core/TeamJSON.java @@ -11,27 +11,29 @@ /** * Team JSON - * + * + * TODO: Remove this class as it is never referenced -- JB + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class TeamJSON extends JSONUtilities { - + private ObjectMapper mapper = new ObjectMapper(); private ObjectNode element = mapper.createObjectNode(); private ArrayNode childNode = mapper.createArrayNode(); - + public String createJSON(IInternalContest contest, Account account) { - + element = mapper.createObjectNode(); childNode = mapper.createArrayNode(); - + // TODO multi-site with overlapping teamNumbers? - - // id - // icpc_id - // name - // organization_id - + + // id + // icpc_id + // name + // organization_id + element.put("id", new Integer(account.getClientId().getClientNumber()).toString()); if (notEmpty(account.getExternalId())) { element.put("icpc_id", account.getExternalId()); @@ -40,14 +42,14 @@ public String createJSON(IInternalContest contest, Account account) { if (notEmpty(account.getInstitutionCode()) && !account.getInstitutionCode().equals("undefined")) { element.put("organization_id", account.getInstitutionCode()); } - if (account.getGroupId() != null) { - Group group = contest.getGroup(account.getGroupId()); + if (account.getPrimaryGroupId() != null) { + Group group = contest.getGroup(account.getPrimaryGroupId()); element.put("group_id", Integer.toString(group.getGroupId())); } - + // rest is provided by CDS, not CCS childNode.add(element); - + return stripOuterJSON(childNode.toString()); } } diff --git a/src/edu/csus/ecs/pc2/services/web/EventFeedFilter.java b/src/edu/csus/ecs/pc2/services/web/EventFeedFilter.java index 640d65057..4e48ec676 100644 --- a/src/edu/csus/ecs/pc2/services/web/EventFeedFilter.java +++ b/src/edu/csus/ecs/pc2/services/web/EventFeedFilter.java @@ -1,57 +1,169 @@ -// 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.services.web; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import edu.csus.ecs.pc2.core.model.Account; +import edu.csus.ecs.pc2.core.model.ElementId; +import edu.csus.ecs.pc2.core.model.Group; +import edu.csus.ecs.pc2.core.model.IInternalContest; +import edu.csus.ecs.pc2.core.model.Problem; import edu.csus.ecs.pc2.services.core.EventFeedJSON; /** * Event Feed filter. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class EventFeedFilter { + private static final String EF_TYPE_STRING = "\"type\":"; + private static final String EF_SEQ_STRING = "\"id\":"; + private static final String DATA_OBJECT_STRING = "\"data\":"; + private static final String PROBLEM_ID_STRING = "\"id\":"; + private static final String CMS_GROUP_ID_STRING = "\"icpc_id\":"; + private static final String TEAM_GROUP_IDS_STRING = "\"group_ids\":"; + private static final String TEAM_ID_STRING = "\"id\":"; + private static final String SUB_TEAM_ID_STRING = "\"team_id\":"; + private static final String SUB_ID_STRING = "\"id\":"; + private static final String JUDGEMENT_ID_STRING = "\"id\":"; + private static final String JUDGEMENT_SUB_ID_STRING = "\"submission_id\":"; + private static final String RUN_JUDGEMENT_ID_STRING = "\"judgement_id\":"; + private static final String CLAR_FROM_TEAM_ID_STRING = "\"from_team_id\":"; + private static final String CLAR_TO_TEAM_ID_STRING = "\"to_team_id\":"; + private static final String CLAR_PROBLEM_ID_STRING = "\"problem_id\":"; + private String eventTypeList = null; private String startingEventId = null; + // List of groups to match events for + private List wantedGroups = null; + private HashSet wantedGroupsSet = null; + private HashSet cmsAffiliatedGroupIds = null; + // Problems that are visible to this filter based on group + private HashSet problemFilter = null; + + // Teams that we are not interested in + private HashSet teamIgnore = new HashSet(); + + // Submissions we are not interested in (so we can filter out judgements for them) + private HashSet subIgnore = new HashSet(); + + // Judgements we are not interseted in (so we can filter out runs (test cases)) + private HashSet judgementIgnore = new HashSet(); + private String clientInfo; - + public EventFeedFilter(){ this(null, null); } - + /** * Create filter. - * - * eventTypeList events are: contests, judgement-types, languages, problems, groups, organizations, - * teams, team-members, submissions, judgements, runs, clarifications, awards. - * + * + * eventTypeList events are: contests, judgement-types, languages, problems, groups, organizations, + * teams, team-members, submissions, judgements, runs, clarifications, awards. + * *
    * The complete list of events are at: {@link EventFeedType} - * + * * @param startingEventId start after event id, null allowed to indicate to not filter * @param eventTypeList eventtype list, null allowed to indicate to not filter */ public EventFeedFilter(String startintEventId, String eventTypeList) { super(); this.startingEventId = startintEventId; - this.eventTypeList = eventTypeList; + if(eventTypeList != null) { + this.eventTypeList = eventTypeList.toUpperCase(); + } else { + this.eventTypeList = null; + } } public void addEventTypeList(String addEventTypeList) { - this.eventTypeList = addEventTypeList; + // user may specify lower case events, eg. "languages,problems" + // we should accept those + this.eventTypeList = addEventTypeList.toUpperCase(); } public void addStartintEventId(String addStartingEventId) { this.startingEventId = addStartingEventId; } - boolean matchesFilter(String eventId, EventFeedType type) { + /** + * Add a list of groups to the event feed filter. Only events appropriate for these groups are sent. + * + * TODO: does not take into account if an account or problem adds a group after the event + * feed has started. This can affect which problems are sent on the event feed. + * @param contest + * @param wantedGroupIds Comma-separated list of group ids + * @return true if the groups were valid, false if any are bad + */ + public boolean addGroups(IInternalContest contest, String wantedGroupIds) { + Account [] accounts = contest.getAccounts(); + HashMap groupToGroupId = new HashMap(); + + // Populate hashmap with for group id to group mapping + for(Group g : contest.getGroups()) { + groupToGroupId.put(Integer.toString(g.getGroupId()), g); + } + for(String groupId : wantedGroupIds.split(",")) { + Group group = groupToGroupId.get(groupId); + if(group == null) { + // Bad group id, abort request + return false; + } + if(wantedGroups == null) { + wantedGroups = new ArrayList(); + wantedGroupsSet = new HashSet(); + cmsAffiliatedGroupIds = new HashSet(); + problemFilter = new HashSet(); + } + wantedGroups.add(group); + wantedGroupsSet.add(Integer.toString(group.getGroupId())); + + ElementId groupElementId = group.getElementId(); + + // now, add all groups for each account that is a member of 'group' + // this is somewhat inefficient + for(Account account : accounts) { + if(account.isGroupMember(groupElementId)) { + // add all groups for this account to the affiliatedGroups + for(ElementId gElementId : account.getGroupIds()) { + cmsAffiliatedGroupIds.add(Integer.toString(contest.getGroup(gElementId).getGroupId())); + } + } + } + + // now create a hashset of all wanted problem short names (id's) for quick look up later + for(Problem problem : contest.getProblems()) { + if(problem.canView(wantedGroups)) { + String id = problem.getShortName(); + // if we don't have a problem shortNamee use the internal id + if (id == null || id.length() == 0) { + id = problem.getElementId().toString(); + } + problemFilter.add(id); + } + } + } + // get rid of empty list - shouldn't happen, but can't hurt to check. + if(wantedGroups != null && wantedGroups.isEmpty()) { + wantedGroups = null; + wantedGroupsSet = null; + cmsAffiliatedGroupIds = null; + problemFilter = null; + } + return(true); + } + + private boolean matchesFilter(String eventId, EventFeedType type) { boolean matched = true; @@ -62,7 +174,9 @@ boolean matchesFilter(String eventId, EventFeedType type) { } if (eventTypeList != null) { - matched &= eventTypeList.indexOf(type.toString()) > -1; + //note: at some point someone added specific (lower/mixed? case) names for the EventFeedType enum + // hence we have to convert it to uppercase since the eventTypeList has been converted to uppercase + matched &= eventTypeList.indexOf(type.toString().toUpperCase()) > -1; } return matched; @@ -70,30 +184,103 @@ boolean matchesFilter(String eventId, EventFeedType type) { /** * match JSON line. - * - * @param string JSON string, ex. + * + * @param string JSON string, ex. * @return */ public boolean matchesFilter(String string) { - if (startingEventId != null || eventTypeList != null) { - return matchesFilter(getEventFeedEequence(string), getEventFeedType(string)); + boolean matches = true; + + EventFeedType recType = EventFeedType.UNDEFINED; + + // always check groups first so we can populate our filtering maps for things like teams' submissions + if(wantedGroups != null) { + // Isolate "data":{} portion + int dataIndex = string.indexOf(DATA_OBJECT_STRING); + + if(dataIndex > 0) { + String rec = getCleanValue(string, EF_TYPE_STRING); + if(rec != null) { + recType = parseEventFeedType(rec); + String dataObject = string.substring(dataIndex + DATA_OBJECT_STRING.length()); + + // The only type of events that require filtering by group are: + // problems, groups, teams, submissions, judgements, runs, clarifications + // a case COULD be made for organizations but, apparently sending extra orgs is ok, ask Fredrik. + switch(recType) { + case PROBLEMS: + matches &= matchesProblem(dataObject); + break; + + case GROUPS: + matches &= matchesGroup(dataObject); + break; + + case TEAMS: + matches &= matchesTeam(dataObject); + break; + + case SUBMISSIONS: + matches &= matchesSubmission(dataObject); + break; + + case JUDGEMENTS: + matches &= matchesJudgement(dataObject); + break; + + case RUNS: + matches &= matchesRun(dataObject); + break; + + case CLARIFICATIONS: + matches &= matchesClar(dataObject); + break; + } + } + } } - return true; + if (matches && (startingEventId != null || eventTypeList != null)) { + // we know that this event would pass through (passed group test), so we now check for event type and token + if(recType == EventFeedType.UNDEFINED) { + String rec = getCleanValue(string, EF_TYPE_STRING); + if(rec != null) { + recType = parseEventFeedType(rec); + } + } + String seqField = getCleanValue(string, EF_SEQ_STRING); + if(seqField != null) { + matches &= matchesFilter(seqField, recType); + } + } + return matches; } /** * Extract event feed type. - * @param string JSON string, {"event":"languages", "id":"pc2-11", "op":"create", "data": {"id":"1","name":"Java"}} + * @param string JSON string, {"type":"languages", "id":"pc2-11", "op":"create", "data": {"id":"1","name":"Java"}} * @return type for event, ex. "languages" as EventFeedType.LANGUAGES. */ protected EventFeedType getEventFeedType(String string) { - // {"event":"teams", "id":"pc2-136", "op":"create", "data": {"id":"114","icpc_id":"3114","name":"team114"}} + // {"type":"languages", "id":"pc2-11", "op":"create", "data": {"id":"1","name":"Java"}} + + String typeValue = getCleanValue(string, EF_TYPE_STRING); + return(parseEventFeedType(typeValue)); + } + + /** + * Convert string to enum for event type + * + * @param typeValue string type of event, eg. teams, problems, submissions, etc. + * @return EventFeedType enumeration + */ + private EventFeedType parseEventFeedType(String typeValue) { + if(typeValue == null) { + return(EventFeedType.UNDEFINED); + } + return EventFeedType.valueOf(typeValue.toUpperCase().replace("-", "_")); - String[] fields = string.split(","); - String fieldValue = fields[0].replaceAll("\"", "").replace("{type:", "").trim(); - return EventFeedType.valueOf(fieldValue.toUpperCase().replace("-", "_")); } /** @@ -101,26 +288,28 @@ protected EventFeedType getEventFeedType(String string) { * @param string ex. {"event":"languages", "id":"pc2-11", "op":"create", "data": {"id":"1","name":"Java"}} * @return value for id, ex pc2-11 */ - protected String getEventFeedEequence(String string) { - // {"event":"teams", "id":"pc2-136", "op":"create", "data": {"id":"114","icpc_id":"3114","name":"team114"}} - String[] fields = string.split(","); - String fieldValue = fields[1].replaceAll("\"", "").replace("id:", "").trim(); - return fieldValue; + protected String getEventFeedSequence(String string) { + // {"event":"languages", "id":"pc2-11", "op":"create", "data": {"id":"1","name":"Java"}} + String seqVal = getCleanValue(string, EF_SEQ_STRING); + if(seqVal == null) { + seqVal = ""; + } + return(seqVal); } - + /** * Filter JSON lines. - * + * * @param jsonLines * @return list of lines matching filter. */ public List filterJson(String [] jsonLines){ return filterJson(jsonLines, this); } - + /** * Filter JSON lines. - * + * * @param jsonLines * @param filter * @return list of lines matching filter. @@ -147,25 +336,260 @@ public String toString() { if (strEventTypeList == null) { strEventTypeList = ""; } - return "startid = " + strStartingEventId + ", event types = " + strEventTypeList; + String strGroupList = ""; + if (wantedGroupsSet != null) { + strGroupList = wantedGroupsSet.toString(); + } + return "startid = " + strStartingEventId + ", event types = " + strEventTypeList + ", groupids = " + strGroupList; } /** * Set identifying information for the client using this filter - * + * * @param string */ public void setClient(String string) { clientInfo = string; } - + /** * Return the identifying information for this user of this filter. - * + * * @return */ public String getClient() { return clientInfo; } + + /** + * Checks if the decorated problem id supplied matches the group filter + * + * @param string json decorated problem id, eg: "{"id":"cornhole-1", ".... + * @return true if the problem matches, false otherwise + */ + private boolean matchesProblem(String string) { + boolean matches = true; + + if(problemFilter != null && string != null) { + + string = getCleanValue(string, PROBLEM_ID_STRING); + if(string != null) { + matches &= problemFilter.contains(string); + } + } + return(matches); + } + + /** + * Checks if the decorated cms group id supplied matches the group filter + * @param string data object from event + * @return true if the group matches, false otherwise + */ + private boolean matchesGroup(String string) { + boolean matches = true; + + String cmsGroupId = getCleanValue(string, CMS_GROUP_ID_STRING); + if(cmsGroupId != null) { + matches &= cmsAffiliatedGroupIds.contains(cmsGroupId); + } + return(matches); + } + + /** + * Checks if the decorated team's group ids supplied matches any in the group filter + * @param string - entire json line + * @return true if the team is a member of any wanted group, false otherwise + */ + private boolean matchesTeam(String string) { + boolean matches = true; + + if(string != null) { + String [] groupIds = getCleanValueArray(string, TEAM_GROUP_IDS_STRING); + + if(groupIds != null && groupIds.length > 0) { + // go through this teams groups, and if any are in the wanted list, accept it and end the loop + matches = false; + for(String cmsGroupId : groupIds) { + if(wantedGroupsSet.contains(cmsGroupId)) { + matches = true; + break; + } + } + } else { + // If no groups on this team, then do not send on feed (we know there is a group filter in place) + matches = false; + } + if(!matches) { + // we are not interested in anything from this team, so keep track of that for submissions and clars + String teamId = getCleanValue(string, TEAM_ID_STRING); + if(teamId != null) { + if(teamId.length() > 0) { + teamIgnore.add(teamId); + } + } + } + } + return(matches); + } + + /** + * Checks if a submission should be shown on EF. This presupposes that accounts (teams) were sent prior to this + * so that the teamIgnore hashset is populated. + * + * @param string containing data portion of event + * @return true if the submission should be sent on EF, false if not + */ + private boolean matchesSubmission(String string) { + boolean matches = true; + + String submitterId = getCleanValue(string, SUB_TEAM_ID_STRING); + if(submitterId != null) { + if(teamIgnore.contains(submitterId)) { + matches = false; + + string = getCleanValue(string, SUB_ID_STRING); + if(string != null) { + subIgnore.add(string); + } + } + } + return(matches); + } + + /** + * Checks if a judgement is visible based on whether we ignored the submission or not + * + * @param string + * @return true if the judgement should be shown, false otherwise + */ + private boolean matchesJudgement(String string) { + boolean matches = true; + + String subId = getCleanValue(string, JUDGEMENT_SUB_ID_STRING); + if(subId != null) { + if(subIgnore.contains(subId)) { + matches = false; + + string = getCleanValue(string, JUDGEMENT_ID_STRING); + if(string != null) { + judgementIgnore.add(string); + } + } + } + return(matches); + } + + /** + * Checks if a run testcase is visible based on whether we ignored the judgement it is part of or not + * + * @param string + * @return true if the run should be shown, false otherwise + */ + private boolean matchesRun(String string) { + boolean matches = true; + + String judgementId = getCleanValue(string, RUN_JUDGEMENT_ID_STRING); + if(judgementId != null) { + if(judgementIgnore.contains(judgementId)) { + matches = false; + } + } + return(matches); + } + + /** + * Checks if a clarification should be visible on the EF. This is harder to check. We show the clar if: + * 1) If either the from or to team is visible on the feed + * 2) If both from and to teams are null and the problem is visible on the feed + * + * @param string + * @return true if the clar should be sent on the feed, false otherwise. + */ + private boolean matchesClar(String string) { + boolean matches = true; + + String fromTeamId = getCleanValue(string, CLAR_FROM_TEAM_ID_STRING); + if(fromTeamId != null && !fromTeamId.equals("null")) { + // from team is not null, so if we are ignoring this team, then we don't show this clar + if(teamIgnore.contains(fromTeamId)) { + matches = false; + } + // else we will always show this clar since the author is visible on the feed + } else { + String toTeamId = getCleanValue(string, CLAR_TO_TEAM_ID_STRING); + if(toTeamId == null || toTeamId.equals("null")) { + // Both from and to teams are null, only show if the problem visible + String problemId = getCleanValue(string, CLAR_PROBLEM_ID_STRING); + if(problemId != null) { + // a 'null' problem id is always allowed (general clar) + if(!problemId.equals("null")) { + matches &= problemFilter.contains(problemId); + } + } + } else if(teamIgnore.contains(toTeamId)){ + matches = false; + } + } + return(matches); + } + + /** + * Finds the supplied json property and isolates the value and cleans it up (removes quotes, braces, lead/trailing spaces) + * + * @param string to find property in + * @return cleaned up property value string otherwise null if not found (no property) + */ + private String getCleanValue(String string, String jsonProperty) { + if(string != null) { + int idx = string.indexOf(jsonProperty); + + if(idx >= 0) { + idx += jsonProperty.length(); + // Find end of this property. None of the properties we care about have commas in them, so this is safe. + int idxEnd = string.indexOf(',', idx); + if(idxEnd > 0) { + string = string.substring(idx, idxEnd); + } else { + string = string.substring(idx); + } + // this is probably ridiculously expensive, what with the RE and all. + // TODO: write optimized routine to remove a set of characters, eg. removeAll(String, String charset) + string = string.replaceAll("[\"{}]", "").trim(); + } + } else { + string = null; + } + return string; + } + + /** + * Finds the supplied json property array and isolates the value and cleans it up (removes quotes, bracket, braces, spaces) + * eg. "group_ids":["35851","1201","1206"]}} -> String [] "35851","1201","1206" + * @param string + * @return cleaned up property array value as string [] otherwise null if not found (no property) + */ + private String [] getCleanValueArray(String string, String jsonProperty) { + String [] groupArray = null; + + if(string != null) { + int idx = string.indexOf(jsonProperty); + + if(idx >= 0) { + idx += jsonProperty.length(); + idx = string.indexOf('[', idx); + // has to be a [ to start the array, if not, we don't care about this + if(idx >= 0) { + idx++; + int idxEnd = string.indexOf(']', idx); + if(idxEnd > 0) { + // this is probably ridiculously expensive, what with the RE and all. + // TODO: write optimized routine to remove a set of characters, eg. removeAll(String, String charset) + groupArray = string.substring(idx, idxEnd).replaceAll("[\"\\s]", "").split(","); + } + } + } + } + return groupArray; + } } diff --git a/src/edu/csus/ecs/pc2/services/web/EventFeedService.java b/src/edu/csus/ecs/pc2/services/web/EventFeedService.java index 2e05462f4..177ee9b10 100644 --- a/src/edu/csus/ecs/pc2/services/web/EventFeedService.java +++ b/src/edu/csus/ecs/pc2/services/web/EventFeedService.java @@ -1,4 +1,4 @@ -// 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.services.web; import java.io.ByteArrayOutputStream; @@ -32,7 +32,7 @@ /** * Implementation of CLICS REST event-feed. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ @Path("/contest/event-feed") @@ -61,12 +61,13 @@ public EventFeedService(IInternalContest inContest, IInternalController inContro /** * a JSON stream representation of the events occurring in the contest. - * + * * @param type * a comma-separated query parameter identifying the type(s) of events being requested (if empty or null, indicates ALL event types) * @param id * the event-id of the earliest event being requested (i.e., an indication of the requested starting point in the event stream) - * + * @param groupids + * a comma-separated list of group ids to return events for * @return a {@link Response} object whose body contains the JSON event feed * @param asyncResponse * @param servletRequest @@ -74,7 +75,7 @@ public EventFeedService(IInternalContest inContest, IInternalController inContro */ @GET @Produces(MediaType.APPLICATION_JSON) - public void streamEventFeed(@QueryParam("types") String eventTypeList, @QueryParam("id") String startintEventId, @Suspended + public void streamEventFeed(@QueryParam("types") String eventTypeList, @QueryParam("id") String startintEventId, @QueryParam("groupids") String wantedGroupIds, @Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest servletRequest, @Context HttpServletResponse response, @Context SecurityContext sc) throws IOException { response.setContentType("json"); @@ -90,23 +91,40 @@ public void streamEventFeed(@QueryParam("types") String eventTypeList, @QueryPar } EventFeedFilter filter = new EventFeedFilter(); - + boolean bEventFeedContraints = false; + + info("starting event feed from " + servletRequest.getRemoteAddr() + " user:" + sc.getUserPrincipal().getName()); if (eventTypeList != null) { filter.addEventTypeList(eventTypeList); - System.out.println("starting event feed, sending only event types '" + eventTypeList + "'"); + info(" sending only event types '" + eventTypeList + "'"); + bEventFeedContraints = true; + } + + // a Comma-separated list of groupids to return events for. + if(wantedGroupIds != null) { + if(filter.addGroups(contest, wantedGroupIds) == false) { + log.log(Level.WARNING, "NOT starting event feed: invalid group(s) specified: " + wantedGroupIds); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid group(s): `" + wantedGroupIds + "`"); + return; + } else { + info(" sending only events appropriate for group(s): " + wantedGroupIds); + bEventFeedContraints = true; + } } if (startintEventId != null) { if (startintEventId.startsWith("pc2-") && Utilities.isIntegerNumber(startintEventId.substring(4))) { filter.addStartintEventId(startintEventId); - System.out.println("starting event feed, Feed starting after id " + startintEventId); + info(" feed starting after id: " + startintEventId); + bEventFeedContraints = true; } else { - System.err.println("NOT starting event feed (invalid startingEventId "+startintEventId+")"); + log.log(Level.WARNING, "NOT starting event feed (invalid startingEventId "+startintEventId+")"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid id: `"+startintEventId+"`"); return; } - } else { - System.out.println("starting event feed (no args) "); + } + if(!bEventFeedContraints) { + info( "entire feed being sent (no filtering)"); } filter.setClient(servletRequest.getRemoteUser() + "@" + servletRequest.getRemoteAddr() + ":" + servletRequest.getRemotePort()); @@ -147,10 +165,10 @@ public boolean configure(FeatureContext arg0) { // TODO Auto-generated method stub return false; } - + /** * Create a snapshot of the JSON event feed. - * + * * @param contest * @param controller * @return diff --git a/src/edu/csus/ecs/pc2/ui/AccountsPane.java b/src/edu/csus/ecs/pc2/ui/AccountsPane.java index 704c64e93..1ef2f68e1 100644 --- a/src/edu/csus/ecs/pc2/ui/AccountsPane.java +++ b/src/edu/csus/ecs/pc2/ui/AccountsPane.java @@ -1,4 +1,4 @@ -// 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.BorderLayout; @@ -9,6 +9,7 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Vector; import javax.swing.JButton; @@ -31,6 +32,7 @@ import edu.csus.ecs.pc2.core.model.AccountEvent; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Filter; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IAccountListener; @@ -41,7 +43,7 @@ /** * Account Pane list. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -59,7 +61,7 @@ public class AccountsPane extends JPanePlugin { private static final String TEXT_DESCRIPTION = "Text (tab delimited) (*.txt,*.tab)"; /** - * + * */ private static final long serialVersionUID = 2297963114219167947L; @@ -92,9 +94,9 @@ public class AccountsPane extends JPanePlugin { private JButton generateAccountsButton = null; private JButton saveButton = null; - + private EditFilterFrame editFilterFrame = null; - + /** * User filter */ @@ -105,7 +107,7 @@ public class AccountsPane extends JPanePlugin { /** * This method initializes - * + * */ public AccountsPane() { super(); @@ -114,7 +116,7 @@ public AccountsPane() { /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); @@ -130,7 +132,7 @@ public String getPluginTitle() { } protected Object[] buildAccountRow(Account account) { -// Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Group", "Alias"}; +// Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Groups", "Alias"}; try { int cols = accountListBox.getColumnCount(); @@ -142,7 +144,7 @@ protected Object[] buildAccountRow(Account account) { s[2] = "" + clientId.getClientNumber(); s[3] = getTeamDisplayName(account); - s[4] = getGroupName(account); + s[4] = getGroupNames(account); s[5] = getTeamAlias(account); return s; } catch (Exception exception) { @@ -151,13 +153,17 @@ protected Object[] buildAccountRow(Account account) { return null; } - private String getGroupName(Account account) { + private String getGroupNames(Account account) { String groupName = ""; - if (account.getGroupId() != null) { - Group group = getContest().getGroup(account.getGroupId()); - if (group != null) { - groupName = group.getDisplayName(); + if (account.getGroupIds() != null) { + ArrayList allGroups = new ArrayList(); + for(ElementId groupElementId : account.getGroupIds()) { + Group group = getContest().getGroup(groupElementId); + if (group != null) { + allGroups.add(group.getDisplayName()); + } } + groupName = String.join(",", allGroups); } return groupName; } @@ -173,7 +179,7 @@ private String getTeamDisplayName(Account account) { return "Invalid Account"; } - + private String getTeamAlias(Account account) { if (account != null) { @@ -188,14 +194,14 @@ private String getTeamAlias(Account account) { /** * This method initializes accountListBox - * + * * @return edu.csus.ecs.pc2.core.log.MCLB */ private MCLB getRunsListBox() { if (accountListBox == null) { accountListBox = new MCLB(); - Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Group", "Alias"}; + Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Groups", "Alias"}; accountListBox.addColumns(cols); // Sorters @@ -237,6 +243,7 @@ public void updateAccountRow(final Account account) { public void updateAccountRow(final Account account, final boolean autoSizeAndSort) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { Object[] objects = buildAccountRow(account); int rowNumber = accountListBox.getIndexByKey(account.getClientId()); @@ -255,7 +262,7 @@ public void run() { /** * Return all accounts for all sites. - * + * * @return Array of all accounts in model. */ private Account[] getAllAccounts() { @@ -269,18 +276,18 @@ private Account[] getAllAccounts() { } } - Account[] accountList = (Account[]) allAccounts.toArray(new Account[allAccounts.size()]); + Account[] accountList = allAccounts.toArray(new Account[allAccounts.size()]); return accountList; } public void reloadAccountList() { getRunsListBox().removeAllRows(); - + Account[] accounts = getAllAccounts(); // TODO bulk load these record - + for (Account account : accounts) { if (! filter.isFilterOn() ){ updateAccountRow(account, false); @@ -288,7 +295,7 @@ public void reloadAccountList() { updateAccountRow(account, false); } } - + if (filter.isFilterOn()){ getFilterButton().setForeground(Color.BLUE); getFilterButton().setToolTipText("Edit filter - filter ON"); @@ -296,11 +303,11 @@ public void reloadAccountList() { getFilterButton().setForeground(Color.BLACK); getFilterButton().setToolTipText("Edit filter"); } - + getRunsListBox().autoSizeAllColumns(); getRunsListBox().sort(); } - + protected void dumpFilter(Filter filter2) { try { @@ -332,6 +339,7 @@ private void updateGUIperPermissions() { getFilterButton().setVisible(true); } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); @@ -344,8 +352,9 @@ public void setContestAndController(IInternalContest inContest, IInternalControl generateAccountsFrame.setContestAndController(inContest, inController); getEditFilterFrame().setContestAndController(inContest, inController); - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadAccountList(); updateGUIperPermissions(); @@ -355,17 +364,19 @@ public void run() { /** * Account Listener Implementation - * + * * @author pc2@ecs.csus.edu - * + * */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { updateAccountRow(accountEvent.getAccount()); } + @Override public void accountModified(AccountEvent accountEvent) { updateAccountRow(accountEvent.getAccount()); @@ -378,6 +389,7 @@ public void accountModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -386,6 +398,7 @@ public void run() { } } + @Override public void accountsAdded(AccountEvent accountEvent) { Account[] accounts = accountEvent.getAccounts(); for (Account account : accounts) { @@ -394,6 +407,7 @@ public void accountsAdded(AccountEvent accountEvent) { sortRunsListBox(); } + @Override public void accountsModified(AccountEvent accountEvent) { Account[] accounts = accountEvent.getAccounts(); for (Account account : accounts) { @@ -406,6 +420,7 @@ public void accountsModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -417,23 +432,25 @@ public void run() { sortRunsListBox(); } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadAccountList(); updateGUIperPermissions(); } }); - + } } /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getButtonPane() { @@ -455,7 +472,7 @@ private JPanel getButtonPane() { /** * This method initializes jButton - * + * * @return javax.swing.JButton */ // TODO enable the Add button when add account works. @@ -467,6 +484,7 @@ private JButton getAddButton() { addButton.setEnabled(true); addButton.setToolTipText("Add new account"); addButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { addAccount(); } @@ -482,7 +500,7 @@ protected void addAccount() { /** * This method initializes jButton1 - * + * * @return javax.swing.JButton */ private JButton getEditButton() { @@ -492,6 +510,7 @@ private JButton getEditButton() { editButton.setMnemonic(KeyEvent.VK_E); editButton.setToolTipText("Edit account"); editButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { editSelectedAccount(); } @@ -521,7 +540,7 @@ protected void editSelectedAccount() { /** * This method initializes filterButton - * + * * @return javax.swing.JButton */ private JButton getFilterButton() { @@ -530,6 +549,7 @@ private JButton getFilterButton() { filterButton.setText("Filter"); filterButton.setMnemonic(KeyEvent.VK_F); filterButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { showFilterAccountsFrame(); } @@ -537,10 +557,11 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } return filterButton; } - + public EditFilterFrame getEditFilterFrame() { if (editFilterFrame == null){ Runnable callback = new Runnable(){ + @Override public void run() { reloadAccountList(); } @@ -549,7 +570,7 @@ public void run() { } return editFilterFrame; } - + protected void showFilterAccountsFrame() { getEditFilterFrame().addList(ListNames.SITES); getEditFilterFrame().addList(ListNames.CLIENT_TYPES); @@ -562,7 +583,7 @@ protected void showFilterAccountsFrame() { /** * This method initializes loadButton - * + * * @return javax.swing.JButton */ private JButton getLoadButton() { @@ -572,6 +593,7 @@ private JButton getLoadButton() { loadButton.setMnemonic(KeyEvent.VK_L); loadButton.setToolTipText("Load Account Information from file"); loadButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { loadAccountsFromDisk(); } @@ -634,7 +656,7 @@ private ReviewAccountLoadFrame getReviewAccountLoadFrame() { /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getMessagePanel() { @@ -653,6 +675,7 @@ private JPanel getMessagePanel() { private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); } @@ -662,6 +685,7 @@ public void run() { private void showMessage(final String string, final Color color) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); messageLabel.setForeground(color); @@ -669,7 +693,7 @@ public void run() { } }); } - + /** * This method invokes the autoSizeAllColumns() and sort() * for the runListBox on the awt thread. @@ -677,6 +701,7 @@ public void run() { */ private void sortRunsListBox() { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { getRunsListBox().autoSizeAllColumns(); getRunsListBox().sort(); @@ -686,7 +711,7 @@ public void run() { /** * This method initializes generateAccountsButton - * + * * @return javax.swing.JButton */ private JButton getGenerateAccountsButton() { @@ -696,6 +721,7 @@ private JButton getGenerateAccountsButton() { generateAccountsButton.setMnemonic(KeyEvent.VK_G); generateAccountsButton.setToolTipText("Generate multiple new accounts"); generateAccountsButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { generateAccountsFrame.setVisible(true); } @@ -706,7 +732,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { /** * This method initializes saveButton - * + * * @return javax.swing.JButton */ private JButton getSaveButton() { @@ -716,6 +742,7 @@ private JButton getSaveButton() { saveButton.setText("Save"); saveButton.setMnemonic(KeyEvent.VK_S); saveButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { saveAccountsToDisk(); } @@ -742,7 +769,7 @@ protected void saveAccountsToDisk() { * the selected file filter. */ FileFilter selectedFilter = null; - + while (true) { showMessage(""); int returnVal = chooser.showSaveDialog(this); diff --git a/src/edu/csus/ecs/pc2/ui/AccountsTablePane.java b/src/edu/csus/ecs/pc2/ui/AccountsTablePane.java index ff439e8b5..f745fd367 100644 --- a/src/edu/csus/ecs/pc2/ui/AccountsTablePane.java +++ b/src/edu/csus/ecs/pc2/ui/AccountsTablePane.java @@ -1,4 +1,4 @@ -// 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.BorderLayout; @@ -19,11 +19,11 @@ import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.RowSorter; import javax.swing.RowSorter.SortKey; import javax.swing.SortOrder; -import javax.swing.JScrollPane; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileFilter; @@ -43,6 +43,7 @@ import edu.csus.ecs.pc2.core.model.AccountEvent; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Filter; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IAccountListener; @@ -53,7 +54,7 @@ /** * Account Pane list. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -62,7 +63,7 @@ public class AccountsTablePane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = -1932790185807184681L; @@ -74,7 +75,7 @@ public class AccountsTablePane extends JPanePlugin { private static final String CSV_DESCRIPTION = "CSV (comma delimited) (*.csv)"; private static final String TEXT_DESCRIPTION = "Text (tab delimited) (*.txt,*.tab)"; - + private static final int VERT_PAD = 2; private static final int HORZ_PAD = 20; @@ -108,11 +109,11 @@ public class AccountsTablePane extends JPanePlugin { private JButton generateAccountsButton = null; private JButton saveButton = null; - + private EditFilterFrame editFilterFrame = null; private JScrollPane scrollPane = null; - + /** * User filter */ @@ -123,7 +124,7 @@ public class AccountsTablePane extends JPanePlugin { /** * This method initializes - * + * */ public AccountsTablePane() { super(); @@ -132,7 +133,7 @@ public AccountsTablePane() { /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); @@ -148,7 +149,7 @@ public String getPluginTitle() { } protected Object[] buildAccountRow(Account account) { -// Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Group", "Alias", "CMS Id", "ClientId" }; +// Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Groups", "Alias", "CMS Id", "ClientId" }; try { int cols = accountTableModel.getColumnCount(); @@ -160,7 +161,7 @@ protected Object[] buildAccountRow(Account account) { s[2] = "" + clientId.getClientNumber(); s[3] = getTeamDisplayName(account); - s[4] = getGroupName(account); + s[4] = getGroupNames(account); s[5] = getTeamAlias(account); s[6] = getExternalId(account); // This column is invisible for the "unique" ID @@ -172,13 +173,17 @@ protected Object[] buildAccountRow(Account account) { return null; } - private String getGroupName(Account account) { + private String getGroupNames(Account account) { String groupName = ""; - if (account.getGroupId() != null) { - Group group = getContest().getGroup(account.getGroupId()); - if (group != null) { - groupName = group.getDisplayName(); + if (account.getGroupIds() != null) { + ArrayList allGroups = new ArrayList(); + for(ElementId groupElementId : account.getGroupIds()) { + Group group = getContest().getGroup(groupElementId); + if (group != null) { + allGroups.add(group.getDisplayName()); + } } + groupName = String.join(",", allGroups); } return groupName; } @@ -194,7 +199,7 @@ private String getTeamDisplayName(Account account) { return "Invalid Account"; } - + private String getTeamAlias(Account account) { if (account != null) { @@ -220,13 +225,13 @@ private String getExternalId(Account account) { /** * This method initializes accountTable and accountTableModel - * + * * @return JTableCustomized */ private JTableCustomized getAccountsTable() { if (accountTable == null) { int i; - Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Group", "Alias", "CMS Id", "ClientId"}; + Object[] cols = { "Site", "Type", "Account Id", "Display Name" , "Groups", "Alias", "CMS Id", "ClientId"}; accountTableModel = new DefaultTableModel(cols, 0) { @Override public boolean isCellEditable(int row, int col) { @@ -234,9 +239,10 @@ public boolean isCellEditable(int row, int col) { } }; TableRowSorter trs = new TableRowSorter(accountTableModel); - + accountTable = new JTableCustomized(accountTableModel); accountTable.addMouseListener(new MouseAdapter() { + @Override public void mouseClicked(MouseEvent me) { if (me.getClickCount() == 2) { // to detect double click events JTable target = (JTable)me.getSource(); @@ -252,10 +258,10 @@ public void mouseClicked(MouseEvent me) { */ TableColumnModel tcm = accountTable.getColumnModel(); tcm.removeColumn(tcm.getColumn(cols.length - 1)); - + accountTable.setRowSorter(trs); accountTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - + ArrayList sortList = new ArrayList(); sortList.add(new RowSorter.SortKey(2, SortOrder.ASCENDING)); sortList.add(new RowSorter.SortKey(1, SortOrder.ASCENDING)); @@ -267,17 +273,17 @@ public void mouseClicked(MouseEvent me) { */ ((DefaultTableCellRenderer)accountTable.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT); accountTable.setRowHeight(accountTable.getRowHeight() + VERT_PAD); - + // Set special compare routines for Account ID, Display Name and CMS Id trs.setComparator(2, new StringToNumberComparator()); trs.setComparator(3, new AccountNameCaseComparator()); trs.setComparator(6, new StringToNumberComparator()); - + resizeColumnWidth(accountTable); } return accountTable; } - + /** * Find row that contains the supplied key (in last column) * @param value - unique key - really, the ClientId of run @@ -285,7 +291,7 @@ public void mouseClicked(MouseEvent me) { */ private int getRowByKey(Object value) { Object o; - + if(accountTableModel != null) { int col = accountTableModel.getColumnCount() - 1; for (int i = accountTableModel.getRowCount() - 1; i >= 0; --i) { @@ -302,7 +308,7 @@ private int getRowByKey(Object value) { * Looks up the unique ID for the account at the supplied table row. * Have to map the row to the underlying tablemodel data first. * The ClientId is stored in the last (invisible) column - * + * * @param nRow - selected row */ private ClientId getClientIdFromTableRow(JTableCustomized table, int nRow) { @@ -311,7 +317,7 @@ private ClientId getClientIdFromTableRow(JTableCustomized table, int nRow) { ClientId clientId = (ClientId) tm.getValueAt(modelIndex, tm.getColumnCount()-1); return(clientId); } - + public void updateAccountRow(final Account account) { updateAccountRow(account, true); } @@ -319,6 +325,7 @@ public void updateAccountRow(final Account account) { public void updateAccountRow(final Account account, final boolean autoSizeAndSort) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { Object[] objects = buildAccountRow(account); int rowNumber = getRowByKey(account.getClientId()); @@ -329,7 +336,7 @@ public void run() { accountTableModel.setValueAt(objects[i], rowNumber, i); } } - + if (autoSizeAndSort) { resizeColumnWidth(accountTable); } @@ -339,7 +346,7 @@ public void run() { /** * Return all accounts for all sites. - * + * * @return Array of all accounts in model. */ private Account[] getAllAccounts() { @@ -353,7 +360,7 @@ private Account[] getAllAccounts() { } } - Account[] accountList = (Account[]) allAccounts.toArray(new Account[allAccounts.size()]); + Account[] accountList = allAccounts.toArray(new Account[allAccounts.size()]); return accountList; } @@ -361,11 +368,11 @@ public void reloadAccountList() { JTableCustomized at = getAccountsTable(); accountTableModel.setNumRows(0); - + Account[] accounts = getAllAccounts(); // TODO bulk load these record - + for (Account account : accounts) { if (! filter.isFilterOn() ){ updateAccountRow(account, false); @@ -373,7 +380,7 @@ public void reloadAccountList() { updateAccountRow(account, false); } } - + if (filter.isFilterOn()){ getFilterButton().setForeground(Color.BLUE); getFilterButton().setToolTipText("Edit filter - filter ON"); @@ -381,10 +388,10 @@ public void reloadAccountList() { getFilterButton().setForeground(Color.BLACK); getFilterButton().setToolTipText("Edit filter"); } - + resizeColumnWidth(at); } - + protected void dumpFilter(Filter filter2) { try { @@ -416,6 +423,7 @@ private void updateGUIperPermissions() { getFilterButton().setVisible(true); } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); @@ -428,8 +436,9 @@ public void setContestAndController(IInternalContest inContest, IInternalControl generateAccountsFrame.setContestAndController(inContest, inController); getEditFilterFrame().setContestAndController(inContest, inController); - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadAccountList(); updateGUIperPermissions(); @@ -439,17 +448,19 @@ public void run() { /** * Account Listener Implementation - * + * * @author pc2@ecs.csus.edu - * + * */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { updateAccountRow(accountEvent.getAccount()); } + @Override public void accountModified(AccountEvent accountEvent) { updateAccountRow(accountEvent.getAccount()); @@ -462,6 +473,7 @@ public void accountModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -470,6 +482,7 @@ public void run() { } } + @Override public void accountsAdded(AccountEvent accountEvent) { Account[] accounts = accountEvent.getAccounts(); for (Account account : accounts) { @@ -478,6 +491,7 @@ public void accountsAdded(AccountEvent accountEvent) { sortAccountsTable(); } + @Override public void accountsModified(AccountEvent accountEvent) { Account[] accounts = accountEvent.getAccounts(); for (Account account : accounts) { @@ -490,6 +504,7 @@ public void accountsModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -501,23 +516,25 @@ public void run() { sortAccountsTable(); } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadAccountList(); updateGUIperPermissions(); } }); - + } } /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getButtonPane() { @@ -539,7 +556,7 @@ private JPanel getButtonPane() { /** * This method initializes jButton - * + * * @return javax.swing.JButton */ // TODO enable the Add button when add account works. @@ -551,6 +568,7 @@ private JButton getAddButton() { addButton.setEnabled(true); addButton.setToolTipText("Add new account"); addButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { addAccount(); } @@ -566,7 +584,7 @@ protected void addAccount() { /** * This method initializes jButton1 - * + * * @return javax.swing.JButton */ private JButton getEditButton() { @@ -576,6 +594,7 @@ private JButton getEditButton() { editButton.setMnemonic(KeyEvent.VK_E); editButton.setToolTipText("Edit account"); editButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { editSelectedAccount(); } @@ -594,7 +613,7 @@ protected void editSelectedAccount() { try { Account accountToEdit = getContest().getAccount(getClientIdFromTableRow(accountTable, selectedIndex)); - + if(accountToEdit != null) { editAccountFrame.setAccount(accountToEdit); editAccountFrame.setVisible(true); @@ -607,7 +626,7 @@ protected void editSelectedAccount() { /** * This method initializes filterButton - * + * * @return javax.swing.JButton */ private JButton getFilterButton() { @@ -616,6 +635,7 @@ private JButton getFilterButton() { filterButton.setText("Filter"); filterButton.setMnemonic(KeyEvent.VK_F); filterButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { showFilterAccountsFrame(); } @@ -623,10 +643,11 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } return filterButton; } - + public EditFilterFrame getEditFilterFrame() { if (editFilterFrame == null){ Runnable callback = new Runnable(){ + @Override public void run() { reloadAccountList(); } @@ -635,7 +656,7 @@ public void run() { } return editFilterFrame; } - + protected void showFilterAccountsFrame() { getEditFilterFrame().addList(ListNames.SITES); getEditFilterFrame().addList(ListNames.CLIENT_TYPES); @@ -648,7 +669,7 @@ protected void showFilterAccountsFrame() { /** * This method initializes loadButton - * + * * @return javax.swing.JButton */ private JButton getLoadButton() { @@ -658,6 +679,7 @@ private JButton getLoadButton() { loadButton.setMnemonic(KeyEvent.VK_L); loadButton.setToolTipText("Load Account Information from file"); loadButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { loadAccountsFromDisk(); } @@ -720,7 +742,7 @@ private ReviewAccountLoadFrame getReviewAccountLoadFrame() { /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getMessagePanel() { @@ -739,6 +761,7 @@ private JPanel getMessagePanel() { private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); } @@ -748,6 +771,7 @@ public void run() { private void showMessage(final String string, final Color color) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); messageLabel.setForeground(color); @@ -755,7 +779,7 @@ public void run() { } }); } - + /** * This method invokes resizeColumnWidth - sorting is automatic. * for the AccountsTable on the awt thread. @@ -763,6 +787,7 @@ public void run() { */ private void sortAccountsTable() { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { resizeColumnWidth(getAccountsTable()); } @@ -771,7 +796,7 @@ public void run() { /** * This method initializes generateAccountsButton - * + * * @return javax.swing.JButton */ private JButton getGenerateAccountsButton() { @@ -781,6 +806,7 @@ private JButton getGenerateAccountsButton() { generateAccountsButton.setMnemonic(KeyEvent.VK_G); generateAccountsButton.setToolTipText("Generate multiple new accounts"); generateAccountsButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { generateAccountsFrame.setVisible(true); } @@ -791,7 +817,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { /** * This method initializes saveButton - * + * * @return javax.swing.JButton */ private JButton getSaveButton() { @@ -801,6 +827,7 @@ private JButton getSaveButton() { saveButton.setText("Save"); saveButton.setMnemonic(KeyEvent.VK_S); saveButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { saveAccountsToDisk(); } @@ -808,10 +835,10 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } return saveButton; } - + /** * This method initializes scrollPane - * + * * @return javax.swing.JScrollPane */ private JScrollPane getScrollPane() { @@ -820,16 +847,17 @@ private JScrollPane getScrollPane() { } return scrollPane; } - + private void resizeColumnWidth(JTableCustomized table) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { TableColumnAdjuster tca = new TableColumnAdjuster(table, HORZ_PAD); tca.adjustColumns(); } }); } - + protected void saveAccountsToDisk() { JFileChooser chooser = new JFileChooser(lastDir); chooser.setDialogTitle("Save accounts to file"); @@ -848,7 +876,7 @@ protected void saveAccountsToDisk() { * the selected file filter. */ FileFilter selectedFilter = null; - + while (true) { showMessage(""); int returnVal = chooser.showSaveDialog(this); diff --git a/src/edu/csus/ecs/pc2/ui/EditAccountPane.java b/src/edu/csus/ecs/pc2/ui/EditAccountPane.java index 833c5c4b9..3d8c2afdc 100644 --- a/src/edu/csus/ecs/pc2/ui/EditAccountPane.java +++ b/src/edu/csus/ecs/pc2/ui/EditAccountPane.java @@ -1,4 +1,4 @@ -// 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.BorderLayout; @@ -10,7 +10,6 @@ import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.Serializable; import java.util.Arrays; import javax.swing.DefaultListModel; @@ -30,6 +29,7 @@ import edu.csus.ecs.pc2.core.IInternalController; import edu.csus.ecs.pc2.core.PermissionGroup; +import edu.csus.ecs.pc2.core.list.GroupComparator; import edu.csus.ecs.pc2.core.list.PermissionByDescriptionComparator; import edu.csus.ecs.pc2.core.list.SiteComparatorBySiteNumber; import edu.csus.ecs.pc2.core.log.Log; @@ -46,7 +46,7 @@ /** * Add/Edit Account Pane - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -55,11 +55,16 @@ public class EditAccountPane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = -1572390105202179281L; - private static final String NONE_SELECTED = "NONE SELECTED"; + public static final String CHECKBOX_GROUP_PROPERTY = "group"; + + // the original height of the jcombobox was 22. the groups jlist is 3 lines, so we added 46(?) + // this makes it easier to make the groups list box bigger without having to change all the + // control offsets below it. + private static final int GROUPS_LIST_HEIGHT = 68; private JPanel messagePane = null; @@ -77,6 +82,8 @@ public class EditAccountPane extends JPanePlugin { private ListModel defaultListModel = new DefaultListModel(); + private ListModel groupsListModel = new DefaultListModel(); + private Log log = null; private JSplitPane mainSplitPane = null; @@ -93,6 +100,10 @@ public class EditAccountPane extends JPanePlugin { private JLabel permissionCountLabel = null; + private JScrollPane groupsScrollPane = null; + + private JCheckBoxJList groupsJList = null; + private JLabel displayNameLabel = null; private JTextField displayNameTextField = null; @@ -107,8 +118,6 @@ public class EditAccountPane extends JPanePlugin { private JLabel groupTitleLabel = null; - private JComboBox groupComboBox = null; - private boolean populatingGUI = false; private Permission permission = new Permission(); @@ -138,7 +147,7 @@ public class EditAccountPane extends JPanePlugin { /** * This method initializes - * + * */ public EditAccountPane() { super(); @@ -147,7 +156,7 @@ public EditAccountPane() { /** * Class to verify numbers. - * + * * @author Troy * */ @@ -172,7 +181,7 @@ public boolean verify(JComponent arg0) { /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); @@ -183,6 +192,7 @@ private void initialize() { this.add(getMainSplitPane(), java.awt.BorderLayout.CENTER); } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); log = getController().getLog(); @@ -191,9 +201,11 @@ public void setContestAndController(IInternalContest inContest, IInternalControl private void addWindowCloserListener() { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { if (getParentFrame() != null) { getParentFrame().addWindowListener(new java.awt.event.WindowAdapter() { + @Override public void windowClosing(java.awt.event.WindowEvent e) { handleCancelButton(); } @@ -203,13 +215,14 @@ public void windowClosing(java.awt.event.WindowEvent e) { }); } + @Override public String getPluginTitle() { return "Edit Account Pane"; } /** * This method initializes messagePane - * + * * @return javax.swing.JPanel */ private JPanel getMessagePane() { @@ -227,7 +240,7 @@ private JPanel getMessagePane() { /** * This method initializes buttonPane - * + * * @return javax.swing.JPanel */ private JPanel getButtonPane() { @@ -245,7 +258,7 @@ private JPanel getButtonPane() { /** * This method initializes addButton - * + * * @return javax.swing.JButton */ private JButton getAddButton() { @@ -254,6 +267,7 @@ private JButton getAddButton() { addButton.setText("Add"); addButton.setEnabled(false); addButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { addAccount(); } @@ -289,7 +303,7 @@ protected void addAccount() { /** * This method initializes updateButton - * + * * @return javax.swing.JButton */ private JButton getUpdateButton() { @@ -299,6 +313,7 @@ private JButton getUpdateButton() { updateButton.setEnabled(false); updateButton.setMnemonic(java.awt.event.KeyEvent.VK_U); updateButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { updateAccount(); } @@ -345,7 +360,7 @@ private boolean validatedFields() { showMessage("Select a site"); return false; } - + if (getDisplayNameTextField().getText().length() == 0){ showMessage("Enter a display name"); return false; @@ -356,7 +371,7 @@ private boolean validatedFields() { /** * This method initializes cancelButton - * + * * @return javax.swing.JButton */ private JButton getCancelButton() { @@ -365,6 +380,7 @@ private JButton getCancelButton() { cancelButton.setText("Cancel"); cancelButton.setMnemonic(java.awt.event.KeyEvent.VK_C); cancelButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { handleCancelButton(); } @@ -411,6 +427,7 @@ public void setAccount(final Account account) { this.account = account; SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { populateGUI(account); enableUpdateButtons(false); @@ -430,14 +447,14 @@ private void populateGUI(Account account2) { getDisplayNameTextField().setText(""); getPasswordTextField().setText(""); getPasswordConfirmField().setText(""); - populateGroupComboBox(null); + populateGroupsList(null); getAddButton().setVisible(true); getUpdateButton().setVisible(false); getAccountTypeComboBox().setSelectedIndex(0); getAccountTypeComboBox().setEnabled(true); - + loadSiteComboBox (getContest().getSiteNumber()); getSiteSelectionComboBox().setEnabled(true); @@ -451,7 +468,7 @@ private void populateGUI(Account account2) { getPasswordTextField().setText(account2.getPassword()); getPasswordConfirmField().setText(account2.getPassword()); - populateGroupComboBox(account2.getGroupId()); + populateGroupsList(account2); getAliasTextField().setText(account2.getAliasName()); populatePermissions(account2); @@ -464,7 +481,7 @@ private void populateGUI(Account account2) { edu.csus.ecs.pc2.core.model.ClientType.Type accountType = account2.getClientId().getClientType(); for (int i = 0; i < getAccountTypeComboBox().getItemCount(); i++) { - ClientType.Type type = (ClientType.Type) getAccountTypeComboBox().getItemAt(i); + ClientType.Type type = getAccountTypeComboBox().getItemAt(i); if (accountType.equals(type)) { getAccountTypeComboBox().setSelectedIndex(i); } @@ -490,11 +507,11 @@ private void populateGUI(Account account2) { } private void loadSiteComboBox(int siteNumber) { - + int selectedIndex = getSiteSelectionComboBox().getSelectedIndex(); - + getSiteSelectionComboBox().removeAllItems(); - + Site[] sites = getContest().getSites(); Arrays.sort(sites, new SiteComparatorBySiteNumber()); for (int i = 0; i < sites.length; i++) { @@ -510,32 +527,65 @@ private void loadSiteComboBox(int siteNumber) { if (selectedIndex != -1) { getSiteSelectionComboBox().setSelectedIndex(selectedIndex); - } + } } - private void populateGroupComboBox(ElementId elementId) { - int groupIndex = 0; - int selectedIndex = 0; - Group[] groups = getContest().getGroups(); - - getGroupComboBox().removeAllItems(); - getGroupComboBox().addItem(NONE_SELECTED); - for (Group group : groups) { - groupIndex++; - getGroupComboBox().addItem(group); - if (elementId != null) { - if (group.getElementId().equals(elementId)) { - selectedIndex = groupIndex; + private void populateGroupsList(Account inAccount) { + + ((DefaultListModel) groupsListModel).removeAllElements(); + + Group [] allgroups = getContest().getGroups(); + Arrays.sort(allgroups, new GroupComparator()); + + // No account, or no groups, just show unchecked list + if (inAccount == null || inAccount.getGroupIds() == null) { + + for (Group group : allgroups) { + addGroupCheckBox(group); + } + getGroupsJList().setSelectedIndex(-1); + + } else { + + int count = 0; + for (Group group : allgroups) { + if (account.isGroupMember(group.getElementId())) { + count++; + } + } + + if (count > 0) { + int[] indexes = new int[count]; + count = 0; + int idx = 0; + for (Group group : allgroups) { + addGroupCheckBox(group); + if (account.isGroupMember(group.getElementId())) { + indexes[count] = idx; + count++; + } + idx++; + } + getGroupsJList().setSelectedIndices(indexes); + getGroupsJList().ensureIndexIsVisible(0); + } else { + for (Group group : allgroups) { + addGroupCheckBox(group); } } } - getGroupComboBox().setSelectedIndex(selectedIndex); + } + + private void addGroupCheckBox(Group group) { + JCheckBox checkBox = new JCheckBox(group.getDisplayName()); + checkBox.putClientProperty(CHECKBOX_GROUP_PROPERTY, group); + ((DefaultListModel) groupsListModel).addElement(checkBox); } private void populatePermissions(Account inAccount) { ((DefaultListModel) defaultListModel).removeAllElements(); - + Permission.Type[] types = Permission.Type.values(); Arrays.sort(types, new PermissionByDescriptionComparator()); @@ -594,6 +644,7 @@ protected void enableUpdateButtons(boolean editedText) { public void showMessage(final String message) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(message); } @@ -602,6 +653,7 @@ public void run() { public void showPermissionCount(final String message) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { permissionCountLabel.setText(message); } @@ -610,7 +662,7 @@ public void run() { /** * This method initializes mainSplitPane - * + * * @return javax.swing.JSplitPane */ private JSplitPane getMainSplitPane() { @@ -618,21 +670,21 @@ private JSplitPane getMainSplitPane() { mainSplitPane = new JSplitPane(); mainSplitPane.setOneTouchExpandable(true); mainSplitPane.setDividerLocation(300); - mainSplitPane.setLeftComponent(getPermissionPane()); - mainSplitPane.setRightComponent(getAccountPane()); + mainSplitPane.setLeftComponent(getAccountPane()); + mainSplitPane.setRightComponent(getPermissionPane()); } return mainSplitPane; } /** * This method initializes permissionPane - * + * * @return javax.swing.JPanel */ - private JPanel getPermissionPane() { + private JPanel getAccountPane() { if (accountDetailPane == null) { aliasLabel = new JLabel(); - aliasLabel.setBounds(new Rectangle(15, 326, 133, 16)); + aliasLabel.setBounds(new Rectangle(15, 304 + GROUPS_LIST_HEIGHT, 133, 16)); aliasLabel.setText("Alias"); siteLabel = new JLabel(); siteLabel.setBounds(new java.awt.Rectangle(155, 221, 128, 16)); @@ -646,7 +698,7 @@ private JPanel getPermissionPane() { jLabel1.setLocation(new java.awt.Point(15, 220)); jLabel1.setSize(new java.awt.Dimension(134, 16)); groupTitleLabel = new JLabel(); - groupTitleLabel.setText("Group"); + groupTitleLabel.setText("Groups"); groupTitleLabel.setLocation(new java.awt.Point(15, 270)); groupTitleLabel.setSize(new java.awt.Dimension(191, 16)); jLabel = new JLabel(); @@ -671,7 +723,7 @@ private JPanel getPermissionPane() { accountDetailPane.add(getPasswordConfirmField(), null); accountDetailPane.add(jLabel, null); accountDetailPane.add(groupTitleLabel, null); - accountDetailPane.add(getGroupComboBox(), null); + accountDetailPane.add(getGroupsScrollPane()); accountDetailPane.add(jLabel1, null); accountDetailPane.add(getAccountTypeComboBox(), null); accountDetailPane.add(accountLabel, null); @@ -682,7 +734,7 @@ private JPanel getPermissionPane() { accountDetailPane.add(getAliasTextField(), null); scoringAdjustmentLabel = new JLabel("Scoring Adjustment"); - scoringAdjustmentLabel.setBounds(15, 380, 271, 20); + scoringAdjustmentLabel.setBounds(15, 358 + GROUPS_LIST_HEIGHT, 271, 20); accountDetailPane.add(scoringAdjustmentLabel); accountDetailPane.add(getScoringAdjustmentTextField(), null); @@ -693,9 +745,10 @@ private JPanel getPermissionPane() { private JTextField getScoringAdjustmentTextField() { if (scoringAdjustmentTextField == null) { scoringAdjustmentTextField = new JTextField(); - scoringAdjustmentTextField.setBounds(15, 405, 271, 22); + scoringAdjustmentTextField.setBounds(15, 383 + GROUPS_LIST_HEIGHT, 271, 22); scoringAdjustmentTextField.setInputVerifier(new NumberVerifier()); scoringAdjustmentTextField.addKeyListener(new java.awt.event.KeyAdapter() { + @Override public void keyReleased(java.awt.event.KeyEvent e) { enableUpdateButton(); } @@ -706,10 +759,10 @@ public void keyReleased(java.awt.event.KeyEvent e) { /** * This method initializes accountPane - * + * * @return javax.swing.JPanel */ - private JPanel getAccountPane() { + private JPanel getPermissionPane() { if (permissionPane == null) { permissionCountLabel = new JLabel(); permissionCountLabel.setText("XX Permissions Selected"); @@ -731,7 +784,7 @@ private JPanel getAccountPane() { /** * This method initializes permissionScrollPane - * + * * @return javax.swing.JScrollPane */ private JScrollPane getPermissionScrollPane() { @@ -744,7 +797,7 @@ private JScrollPane getPermissionScrollPane() { /** * This method initializes permissionsJList - * + * * @return javax.swing.JList */ private JCheckBoxJList getPermissionsJList() { @@ -753,6 +806,7 @@ private JCheckBoxJList getPermissionsJList() { permissionsJList.setModel(defaultListModel); // ListSelectionListeners are called before JCheckBoxes get updated permissionsJList.addPropertyChangeListener("change", new PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent evt) { showPermissionCount(permissionsJList.getSelectedIndices().length + " permissions selected"); enableUpdateButton(); @@ -764,7 +818,7 @@ public void propertyChange(PropertyChangeEvent evt) { /** * This method initializes displayNameTextField - * + * * @return javax.swing.JTextField */ private JTextField getDisplayNameTextField() { @@ -772,6 +826,7 @@ private JTextField getDisplayNameTextField() { displayNameTextField = new JTextField(); displayNameTextField.setBounds(new java.awt.Rectangle(14, 88, 272, 22)); displayNameTextField.addKeyListener(new java.awt.event.KeyAdapter() { + @Override public void keyReleased(java.awt.event.KeyEvent e) { enableUpdateButton(); } @@ -782,7 +837,7 @@ public void keyReleased(java.awt.event.KeyEvent e) { /** * This method initializes passwordTextField - * + * * @return javax.swing.JTextField */ private JTextField getPasswordTextField() { @@ -790,6 +845,7 @@ private JTextField getPasswordTextField() { passwordTextField = new JTextField(); passwordTextField.setBounds(new Rectangle(14, 140, 272, 22)); passwordTextField.addKeyListener(new java.awt.event.KeyAdapter() { + @Override public void keyReleased(java.awt.event.KeyEvent e) { enableUpdateButton(); } @@ -800,7 +856,7 @@ public void keyReleased(java.awt.event.KeyEvent e) { /** * This method initializes jTextField - * + * * @return javax.swing.JTextField */ private JTextField getPasswordConfirmField() { @@ -808,6 +864,7 @@ private JTextField getPasswordConfirmField() { passwordConfirmField = new JTextField(); passwordConfirmField.setBounds(new Rectangle(14, 191, 272, 22)); passwordConfirmField.addKeyListener(new java.awt.event.KeyAdapter() { + @Override public void keyReleased(java.awt.event.KeyEvent e) { enableUpdateButton(); } @@ -817,21 +874,38 @@ public void keyReleased(java.awt.event.KeyEvent e) { } /** - * This method initializes jTextField - * - * @return javax.swing.JTextField + * This method initializes groups ScrollPane + * + * @return javax.swing.JScrollPane */ - private JComboBox getGroupComboBox() { - if (groupComboBox == null) { - groupComboBox = new JComboBox(); - groupComboBox.setBounds(new java.awt.Rectangle(14, 291, 272, 22)); - groupComboBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { + private JScrollPane getGroupsScrollPane() { + if (groupsScrollPane == null) { + groupsScrollPane = new JScrollPane(); + groupsScrollPane.setBounds(new java.awt.Rectangle(14, 291, 272, GROUPS_LIST_HEIGHT)); + groupsScrollPane.setViewportView(getGroupsJList()); + } + return groupsScrollPane; + } + + /** + * This method initializes groupsJList + * + * @return javax.swing.JList + */ + private JCheckBoxJList getGroupsJList() { + if (groupsJList == null) { + groupsJList = new JCheckBoxJList(); + groupsJList.setModel(groupsListModel); + + // ListSelectionListeners are called before JCheckBoxes get updated + groupsJList.addPropertyChangeListener("change", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { enableUpdateButton(); } }); } - return groupComboBox; + return groupsJList; } public void enableUpdateButton() { @@ -881,7 +955,7 @@ public void enableUpdateButton() { private Account getAccountFromFields(Account checkAccount) { Site site = (Site) getSiteSelectionComboBox().getSelectedItem(); - + if (checkAccount == null) { if (account == null) { ClientType.Type clientType = (ClientType.Type) getAccountTypeComboBox().getSelectedItem(); @@ -911,11 +985,57 @@ private Account getAccountFromFields(Account checkAccount) { // get display name and group checkAccount.setDisplayName(getDisplayNameTextField().getText()); - if (getGroupComboBox().getSelectedIndex() > 0) { - Group group = (Group) getGroupComboBox().getSelectedItem(); - checkAccount.setGroupId(group.getElementId()); - } else { - checkAccount.setGroupId(null); + + // Currently selected groups on the GUI + Object[] gobjects = getGroupsJList().getSelectedValues(); + + // remember primary group from account, if any + ElementId primaryGroup = checkAccount.getPrimaryGroupId(); + + // We need a heuristic here to determine what to use as the primary group if + // there was none to start with. Perhaps the one with the longest display name. + // Yes, we'll go with that for now, but it is a bit of a hack. + // This next block of code is here just to handle this. -- JB + int maxGroupNameLen = 0; + ElementId pickPrimaryGroup = null; + boolean primaryFound = false; + + // find one with longest name of selected groups, also, checking if the current + // primary group is, in fact, still selected on the GUI. + for (Object object : gobjects) { + JCheckBox groupCheck = (JCheckBox)object; + Group group = (Group)groupCheck.getClientProperty(CHECKBOX_GROUP_PROPERTY); + if(group != null) { + String dispName = group.getDisplayName(); + ElementId groupElementId = group.getElementId(); + + // Keep track of longest group name for picking a primary group if needed. + if(dispName.length() > maxGroupNameLen) { + maxGroupNameLen = dispName.length(); + pickPrimaryGroup = groupElementId; + } + + if(groupElementId == primaryGroup) { + primaryFound = true; + } + } + } + // if no primary group, then use the one we picked, if any + if(!primaryFound) { + primaryGroup = pickPrimaryGroup; + } + + // clear out the groups + checkAccount.clearGroups(); + // now add each selected group to the account + for (Object object : gobjects) { + JCheckBox groupCheck = (JCheckBox)object; + Group group = (Group)groupCheck.getClientProperty(CHECKBOX_GROUP_PROPERTY); + if(group != null) { + ElementId groupElementId = group.getElementId(); + boolean bPrimary = (groupElementId == primaryGroup); + checkAccount.addGroupId(groupElementId, bPrimary); + } } checkAccount.setSiteNumber(site.getSiteNumber()); @@ -933,7 +1053,7 @@ private Account getAccountFromFields(Account checkAccount) { /** * Return Permission Type from description string. - * + * * @param string * @return */ @@ -948,7 +1068,7 @@ private Type getTypeFromDescrption(String string) { /** * Intialize Account Type combo box - * + * * @return javax.swing.JComboBox */ private JComboBox getAccountTypeComboBox() { @@ -969,7 +1089,7 @@ private JComboBox getAccountTypeCom /** * This method initializes accountTextField - * + * * @return javax.swing.JTextField */ private JTextField getAccountTextField() { @@ -984,7 +1104,7 @@ private JTextField getAccountTextField() { /** * This method initializes siteSelectionComboBox - * + * * @return javax.swing.JComboBox */ private JComboBox getSiteSelectionComboBox() { @@ -997,15 +1117,16 @@ private JComboBox getSiteSelectionComboBox() { /** * This method initializes aliasTextField - * + * * @return javax.swing.JTextField */ private JTextField getAliasTextField() { if (aliasTextField == null) { aliasTextField = new JTextField(); - aliasTextField.setLocation(new Point(14, 351)); + aliasTextField.setLocation(new Point(14, 329 + GROUPS_LIST_HEIGHT)); aliasTextField.setSize(new Dimension(272, 22)); aliasTextField.addKeyListener(new java.awt.event.KeyAdapter() { + @Override public void keyReleased(java.awt.event.KeyEvent e) { enableUpdateButton(); } @@ -1016,7 +1137,7 @@ public void keyReleased(java.awt.event.KeyEvent e) { /** * This method initializes permButtonPane - * + * * @return javax.swing.JPanel */ private JPanel getPermButtonPane() { @@ -1034,7 +1155,7 @@ private JPanel getPermButtonPane() { /** * This method initializes resetPermissionsButton - * + * * @return javax.swing.JButton */ private JButton getResetPermissionsButton() { @@ -1044,6 +1165,7 @@ private JButton getResetPermissionsButton() { resetPermissionsButton.setToolTipText("Reset Default Permission"); resetPermissionsButton.setMnemonic(KeyEvent.VK_R); resetPermissionsButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { resetPermissions(); } @@ -1054,7 +1176,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { /** * Get default permissions and set them into listbox. - * + * */ protected void resetPermissions() { Account fakeAccount = new Account(account.getClientId(), account.getPassword(), account.getSiteNumber()); diff --git a/src/edu/csus/ecs/pc2/ui/EditFilterPane.java b/src/edu/csus/ecs/pc2/ui/EditFilterPane.java index 9dece06ec..21665541d 100644 --- a/src/edu/csus/ecs/pc2/ui/EditFilterPane.java +++ b/src/edu/csus/ecs/pc2/ui/EditFilterPane.java @@ -1,4 +1,4 @@ -// 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.BorderLayout; @@ -19,6 +19,7 @@ import edu.csus.ecs.pc2.core.IInternalController; import edu.csus.ecs.pc2.core.list.AccountComparator; +import edu.csus.ecs.pc2.core.list.GroupComparator; import edu.csus.ecs.pc2.core.list.PermissionByDescriptionComparator; import edu.csus.ecs.pc2.core.list.SiteComparatorBySiteNumber; import edu.csus.ecs.pc2.core.model.Account; @@ -30,8 +31,10 @@ import edu.csus.ecs.pc2.core.model.ContestInformation; import edu.csus.ecs.pc2.core.model.ContestInformationEvent; import edu.csus.ecs.pc2.core.model.DisplayTeamName; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.Filter; import edu.csus.ecs.pc2.core.model.FilterFormatter; +import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IContestInformationListener; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Judgement; @@ -43,7 +46,7 @@ /** * Edit Filter GUI. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -52,7 +55,7 @@ public class EditFilterPane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = 1866852944568248601L; @@ -66,6 +69,8 @@ public class EditFilterPane extends JPanePlugin { private JPanel teamsPane = null; // @jve:decl-index=0:visual-constraint="589,115" + private JPanel groupsPane = null; // @jve:decl-index=0:visual-constraint="589,115" + private JPanel judgementsPane = null; // @jve:decl-index=0:visual-constraint="349,385" private JPanel listsPanel = null; @@ -76,6 +81,8 @@ public class EditFilterPane extends JPanePlugin { private JScrollPane teamsScroll = null; + private JScrollPane groupsScroll = null; + private JScrollPane problemsScroll = null; private JScrollPane languagesScroll = null; @@ -88,6 +95,10 @@ public class EditFilterPane extends JPanePlugin { private DefaultListModel teamListModel = new DefaultListModel(); + private JCheckBoxJList groupListBox = null; + + private DefaultListModel groupListModel = new DefaultListModel(); + private JCheckBoxJList problemsListBox = null; private DefaultListModel problemListModel = new DefaultListModel(); @@ -105,13 +116,13 @@ public class EditFilterPane extends JPanePlugin { private JCheckBoxJList runStatesListBox = null; private DefaultListModel runStatesListModel = new DefaultListModel(); - + private DefaultListModel clarificationStatesListModel = new DefaultListModel(); private DefaultListModel sitesListModel = new DefaultListModel(); private DefaultListModel permissionsListModel = new DefaultListModel(); - + private JPanel timeRangePane = null; private JLabel fromTimeLabel = null; @@ -121,11 +132,11 @@ public class EditFilterPane extends JPanePlugin { private JLabel toTimeLabel = null; private JTextField toTimeTextField = null; - + private DisplayTeamName displayTeamName = null; // @jve:decl-index=0: private boolean isJudgeModule = false; - + private boolean filteringClarifications = false; private JPanel clarificationStatesPane = null; // @jve:decl-index=0:visual-constraint="642,361" @@ -136,37 +147,37 @@ public class EditFilterPane extends JPanePlugin { private JPanel sitesPane = null; // @jve:decl-index=0:visual-constraint="649,76" - private JPanel permissionsPane = null; + private JPanel permissionsPane = null; private JScrollPane sitesScroll = null; - + private JScrollPane permissionScroll = null; private JCheckBoxJList siteListBox = null; - + private JCheckBoxJList permissionListBox = null; - + private JPanel accountsPane = null; private DefaultListModel accountListModel = new DefaultListModel(); private JCheckBoxJList accountListBox = null; - + private JScrollPane accountScroll = null; private JPanel clientTypePane = null; private JScrollPane clientTypeScroll = null; - + private DefaultListModel clientTypeListModel = new DefaultListModel(); private JCheckBoxJList clientTypesListBox; // @jve:decl-index=0: private Permission permission = new Permission(); - + /** * JList names in EditFilterPane. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -191,17 +202,23 @@ public enum ListNames { /** * Clarification States JList. */ - CLARIFICATION_STATES, + CLARIFICATION_STATES, /** * Accounts JList. */ ALL_ACCOUNTS, /** - * + * */ TEAM_ACCOUNTS, + + /** + * + */ + GROUPS, + /** - * Elapsed Time (both From and To) + * Elapsed Time (both From and To) */ TIME_RANGE, /** @@ -209,11 +226,11 @@ public enum ListNames { */ SITES, /** - * + * */ PERMISSIONS, /** - * + * */ CLIENT_TYPES, } @@ -236,7 +253,7 @@ private void initialize() { this.setSize(new java.awt.Dimension(493, 337)); this.add(getMainPane(), java.awt.BorderLayout.CENTER); } - + @Override public String getPluginTitle() { return "Edit Filter"; @@ -244,7 +261,7 @@ public String getPluginTitle() { /** * This method initializes filterOnCheckBox - * + * * @return javax.swing.JCheckBox */ private JCheckBox getFilterOnCheckBox() { @@ -258,7 +275,7 @@ private JCheckBox getFilterOnCheckBox() { /** * This method initializes problemFrame - * + * * @return javax.swing.JPanel */ private JPanel getProblemsPane() { @@ -275,7 +292,7 @@ private JPanel getProblemsPane() { /** * This method initializes bottomPanel - * + * * @return javax.swing.JPanel */ private JPanel getBottomPanel() { @@ -288,7 +305,7 @@ private JPanel getBottomPanel() { /** * This method initializes languagePane - * + * * @return javax.swing.JPanel */ private JPanel getLanguagesPane() { @@ -304,8 +321,8 @@ private JPanel getLanguagesPane() { } /** - * This method initializes teamFrame - * + * This method initializes teamsFrame + * * @return javax.swing.JPanel */ private JPanel getTeamsPane() { @@ -319,10 +336,27 @@ private JPanel getTeamsPane() { } return teamsPane; } - + + /** + * This method initializes groupsFrame + * + * @return javax.swing.JPanel + */ + private JPanel getGroupsPane() { + if (groupsPane == null) { + groupsPane = new JPanel(); + groupsPane.setLayout(new BorderLayout()); + groupsPane.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Groups", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, + null, null)); + groupsPane.setName("groupFrame"); + groupsPane.add(getGroupsScroll(), java.awt.BorderLayout.CENTER); + } + return groupsPane; + } + /** - * This method initializes teamFrame - * + * This method initializes accountsFrame + * * @return javax.swing.JPanel */ private JPanel getAccountsPane() { @@ -339,7 +373,7 @@ private JPanel getAccountsPane() { /** * This method initializes judgementFrame - * + * * @return javax.swing.JPanel */ private JPanel getJudgementsPane() { @@ -356,7 +390,7 @@ private JPanel getJudgementsPane() { /** * This method initializes otherPanel - * + * * @return javax.swing.JPanel */ private JPanel getListsPanel() { @@ -365,7 +399,7 @@ private JPanel getListsPanel() { gridLayout.setRows(1); listsPanel = new JPanel(); listsPanel.setLayout(gridLayout); - + // TODO remove this // listsPanel.add(getTimeRangePane(), null); } @@ -374,7 +408,7 @@ private JPanel getListsPanel() { /** * This method initializes mainPane - * + * * @return javax.swing.JPanel */ private JPanel getMainPane() { @@ -389,7 +423,7 @@ private JPanel getMainPane() { /** * This method initializes judgementsScroll - * + * * @return javax.swing.JScrollPane */ private JScrollPane getJudgementsScroll() { @@ -402,7 +436,7 @@ private JScrollPane getJudgementsScroll() { /** * This method initializes teamsScroll - * + * * @return javax.swing.JScrollPane */ private JScrollPane getTeamsScroll() { @@ -414,8 +448,21 @@ private JScrollPane getTeamsScroll() { } /** - * This method initializes teamsScroll - * + * This method initializes groupsScroll + * + * @return javax.swing.JScrollPane + */ + private JScrollPane getGroupsScroll() { + if (groupsScroll == null) { + groupsScroll = new JScrollPane(); + groupsScroll.setViewportView(getGroupListBox()); + } + return groupsScroll; + } + + /** + * This method initializes accountsScroll + * * @return javax.swing.JScrollPane */ private JScrollPane getAccountScroll() { @@ -427,7 +474,7 @@ private JScrollPane getAccountScroll() { } /** * This method initializes problemsScroll - * + * * @return javax.swing.JScrollPane */ private JScrollPane getProblemsScroll() { @@ -440,7 +487,7 @@ private JScrollPane getProblemsScroll() { /** * This method initializes languagesScroll - * + * * @return javax.swing.JScrollPane */ private JScrollPane getLanguagesScroll() { @@ -453,7 +500,7 @@ private JScrollPane getLanguagesScroll() { /** * This method initializes judgementListBox - * + * * @return javax.swing.JList */ private JCheckBoxJList getJudgementListBox() { @@ -465,7 +512,7 @@ private JCheckBoxJList getJudgementListBox() { /** * This method initializes teamListBox - * + * * @return javax.swing.JTextArea */ private JCheckBoxJList getTeamListBox() { @@ -474,10 +521,22 @@ private JCheckBoxJList getTeamListBox() { } return teamListBox; } - + + /** + * This method initializes groupListBox + * + * @return javax.swing.JTextArea + */ + private JCheckBoxJList getGroupListBox() { + if (groupListBox == null) { + groupListBox = new JCheckBoxJList(groupListModel); + } + return groupListBox; + } + /** * This method initializes teamListBox - * + * * @return javax.swing.JTextArea */ private JCheckBoxJList getAccountListBox() { @@ -486,11 +545,11 @@ private JCheckBoxJList getAccountListBox() { } return accountListBox; } - + /** * This method initializes problemsListBox - * + * * @return javax.swing.JList */ private JCheckBoxJList getProblemsListBox() { @@ -502,7 +561,7 @@ private JCheckBoxJList getProblemsListBox() { /** * This method initializes languagesListBox - * + * * @return javax.swing.JList */ private JCheckBoxJList getLanguagesListBox() { @@ -514,7 +573,7 @@ private JCheckBoxJList getLanguagesListBox() { /** * Populate the values for all JLists. - * + * */ public void populateFields() { @@ -530,10 +589,10 @@ public void populateFields() { wrapperJCheckBox.setSelected(filter.matches(category)); } problemListModel.addElement(wrapperJCheckBox); - + } } - + for (Problem problem : getContest().getProblems()) { WrapperJCheckBox wrapperJCheckBox = new WrapperJCheckBox(problem); if (filter.isFilteringProblems()) { @@ -550,7 +609,7 @@ public void populateFields() { } languageListModel.addElement(wrapperJCheckBox); } - + sitesListModel.removeAllElements(); Site [] sites = getContest().getSites(); Arrays.sort (sites, new SiteComparatorBySiteNumber()); @@ -561,8 +620,8 @@ public void populateFields() { } sitesListModel.addElement(wrapperJCheckBox); } - - + + permissionsListModel.removeAllElements(); Permission.Type[] types = Permission.Type.values(); Arrays.sort(types, new PermissionByDescriptionComparator()); @@ -573,7 +632,7 @@ public void populateFields() { } permissionsListModel.addElement(wrapperJCheckBox); } - + clientTypeListModel.removeAllElements(); for (Type type : Type.values()) { WrapperJCheckBox wrapperJCheckBox = new WrapperJCheckBox(type); @@ -591,11 +650,13 @@ public void populateFields() { } judgementListModel.addElement(wrapperJCheckBox); } - + loadTeamNames (filter); - + + loadGroupNames (filter); + loadAccountNames(filter); - + runStatesListModel.removeAllElements(); RunStates[] runStates = RunStates.values(); for (RunStates runState : runStates) { @@ -615,7 +676,7 @@ public void populateFields() { } clarificationStatesListModel.addElement(wrapperJCheckBox); } - + getFromTimeTextField().setText(""); getToTimeTextField().setText(""); if (filter.isFilteringElapsedTime()) { @@ -627,16 +688,16 @@ public void populateFields() { } } } - + /** * Populate the team names when with display mask. - * + * * This method also retains and re-populates the teams selected * not based on the input filter, but based on what the user has * selected. */ protected void populateTeamNamesWithDisplayMask(){ - + if (isJudgeModule) { ContestInformation contestInformation = getContest().getContestInformation(); @@ -668,7 +729,7 @@ protected void populateTeamNamesWithDisplayMask(){ private void loadTeamNames(Filter inFilter) { Vector vector = getContest().getAccounts(ClientType.Type.TEAM); - Account[] accounts = (Account[]) vector.toArray(new Account[vector.size()]); + Account[] accounts = vector.toArray(new Account[vector.size()]); Arrays.sort(accounts, new AccountComparator()); teamListModel.removeAllElements(); @@ -686,6 +747,22 @@ private void loadTeamNames(Filter inFilter) { } } + private void loadGroupNames(Filter inFilter) { + Group [] groups = getContest().getGroups(); + Arrays.sort(groups, new GroupComparator()); + + groupListModel.removeAllElements(); + WrapperJCheckBox wrapperJCheckBox = null; + for (Group group : groups) { + ElementId groupElementId = group.getElementId(); + wrapperJCheckBox = new WrapperJCheckBox(groupElementId, group.getDisplayName()); + if (inFilter.isFilteringGroups()) { + wrapperJCheckBox.setSelected(inFilter.matches(group)); + } + groupListModel.addElement(wrapperJCheckBox); + } + } + private void loadAccountNames(Filter inFilter) { Account[] accounts = getContest().getAccounts(); Arrays.sort(accounts, new AccountComparator()); @@ -707,17 +784,18 @@ private void loadAccountNames(Filter inFilter) { } - + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); - + getContest().addContestInformationListener(new ContestInformationListenerImplementation()); isJudgeModule = getContest().getClientId().getClientType().equals(Type.JUDGE); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { populateFields(); } @@ -759,7 +837,7 @@ public Filter getFilter() { filter.addAccount((ClientId) object); } } - + enumeration = accountListModel.elements(); while (enumeration.hasMoreElements()) { WrapperJCheckBox element = (WrapperJCheckBox) enumeration.nextElement(); @@ -768,7 +846,7 @@ public Filter getFilter() { filter.addAccount((ClientId) object); } } - + filter.clearRunStatesList(); enumeration = runStatesListModel.elements(); @@ -789,7 +867,7 @@ public Filter getFilter() { filter.addClarificationState((ClarificationStates) object); } } - + filter.clearJudgementList(); enumeration = judgementListModel.elements(); while (enumeration.hasMoreElements()) { @@ -799,7 +877,7 @@ public Filter getFilter() { filter.addJudgement((Judgement) object); } } - + filter.clearSiteList(); enumeration = sitesListModel.elements(); while (enumeration.hasMoreElements()) { @@ -809,7 +887,7 @@ public Filter getFilter() { filter.addSite((Site) object); } } - + filter.clearPermissionsList(); enumeration = permissionsListModel.elements(); while (enumeration.hasMoreElements()) { @@ -819,7 +897,18 @@ public Filter getFilter() { filter.addPermission((Permission.Type) object); } } - + + filter.clearGroupsList(); + enumeration = groupListModel.elements(); + while (enumeration.hasMoreElements()) { + WrapperJCheckBox element = (WrapperJCheckBox) enumeration.nextElement(); + if (element.isSelected()) { + Object object = element.getContents(); + filter.addGroup((ElementId)object); + } + } + + filter.clearClientTypesList(); enumeration = clientTypeListModel.elements(); while (enumeration.hasMoreElements()) { @@ -834,22 +923,23 @@ public Filter getFilter() { if (getFromTimeTextField().getText().length() > 0){ filter.setStartElapsedTime(Long.parseLong(getFromTimeTextField().getText())); } - + if (getToTimeTextField().getText().length() > 0){ filter.setEndElapsedTime(Long.parseLong(getToTimeTextField().getText())); } - + return filter; } /** * Assigns filter and repopulates fields. - * + * * @param filter */ public void setFilter(Filter filter) { this.filter = filter; SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { populateFields(); } @@ -861,7 +951,7 @@ protected void printAllSpecifiers(String prefix, IInternalContest contest, Filte FilterFormatter.NUMBER_JUDGEMENTS_SPECIFIER, FilterFormatter.NUMBER_LANGUAGES_SPECIFIER, FilterFormatter.NUMBER_PROBLEMS_SPECIFIER, FilterFormatter.PROBLEMS_SPECIFIER, FilterFormatter.SHORT_ACCOUNT_NAMES_SPECIFIER, FilterFormatter.TEAM_LIST_SPECIFIER, FilterFormatter.TEAM_LONG_LIST_SPECIFIER, FilterFormatter.START_TIME_RANGE_SPECIFIER, FilterFormatter.END_TIME_RANGE_SPECIFIER }; - + Arrays.sort(names); FilterFormatter filterFormatter = new FilterFormatter(); @@ -873,7 +963,7 @@ protected void printAllSpecifiers(String prefix, IInternalContest contest, Filte /** * This method initializes jScrollPane - * + * * @return javax.swing.JScrollPane */ private JScrollPane getJScrollPane() { @@ -886,7 +976,7 @@ private JScrollPane getJScrollPane() { /** * This method initializes runStatesPane - * + * * @return javax.swing.JPanel */ private JPanel getRunStatesPane() { @@ -902,7 +992,7 @@ private JPanel getRunStatesPane() { /** * This method initializes runStatesListBox - * + * * @return edu.csus.ecs.pc2.ui.JCheckBoxJList */ private JCheckBoxJList getRunStatesListBox() { @@ -911,10 +1001,10 @@ private JCheckBoxJList getRunStatesListBox() { } return runStatesListBox; } - + /** * Add a list to the criteria. - * + * * @param listName */ public void addList (ListNames listName){ @@ -925,6 +1015,9 @@ public void addList (ListNames listName){ case TEAM_ACCOUNTS: listsPanel.add(getTeamsPane(), 0); break; + case GROUPS: + listsPanel.add(getGroupsPane(), 0); + break; case LANGUAGES: listsPanel.add(getLanguagesPane(), 0); break; @@ -959,7 +1052,7 @@ public void addList (ListNames listName){ /** * This method initializes timeRangePane - * + * * @return javax.swing.JPanel */ private JPanel getTimeRangePane() { @@ -981,7 +1074,7 @@ private JPanel getTimeRangePane() { /** * This method initializes fromTimeTextField - * + * * @return javax.swing.JTextField */ private JTextField getFromTimeTextField() { @@ -995,7 +1088,7 @@ private JTextField getFromTimeTextField() { /** * This method initializes toTimeTextField - * + * * @return javax.swing.JTextField */ private JTextField getToTimeTextField() { @@ -1014,35 +1107,40 @@ public DisplayTeamName getDisplayTeamName() { public void setDisplayTeamName(DisplayTeamName displayTeamName) { this.displayTeamName = displayTeamName; } - + /** * Contest Listener for Edit Filter Pane. - * - * This listens for changes in the way the team display is to + * + * This listens for changes in the way the team display is to * displayed aka the Team Information Displayed to Judges setting - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ public class ContestInformationListenerImplementation implements IContestInformationListener { + @Override public void contestInformationAdded(ContestInformationEvent event) { populateTeamNamesWithDisplayMask(); } + @Override public void contestInformationChanged(ContestInformationEvent event) { populateTeamNamesWithDisplayMask(); } + @Override public void contestInformationRemoved(ContestInformationEvent event) { populateTeamNamesWithDisplayMask(); } + @Override public void contestInformationRefreshAll(ContestInformationEvent contestInformationEvent) { populateTeamNamesWithDisplayMask(); } + @Override public void finalizeDataChanged(ContestInformationEvent contestInformationEvent) { // Not used } @@ -1050,7 +1148,7 @@ public void finalizeDataChanged(ContestInformationEvent contestInformationEvent) /** * This method initializes clarificationsPane - * + * * @return javax.swing.JPanel */ private JPanel getClarificationStatesPane() { @@ -1067,7 +1165,7 @@ private JPanel getClarificationStatesPane() { /** * This method initializes clarificationStateScrollPane - * + * * @return javax.swing.JScrollPane */ private JScrollPane getClarificationStateScrollPane() { @@ -1080,7 +1178,7 @@ private JScrollPane getClarificationStateScrollPane() { /** * This method initializes clarificationStatesListBox - * + * * @return javax.swing.JList */ private JList getClarificationStatesListBox() { @@ -1100,7 +1198,7 @@ public void setFilteringClarifications(boolean filteringClarifications) { /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getSitesPane() { @@ -1115,8 +1213,8 @@ private JPanel getSitesPane() { } return sitesPane; } - - + + public JPanel getClientTypePane() { if (clientTypePane == null) { clientTypePane = new JPanel(); @@ -1146,8 +1244,8 @@ private JCheckBoxJList getClientTypesListBox () { return clientTypesListBox; } - - + + public JPanel getPermissionsPane() { if (permissionsPane == null) { permissionsPane = new JPanel(); @@ -1161,7 +1259,7 @@ public JPanel getPermissionsPane() { return permissionsPane; } - + /** * @return javax.swing.JScrollPane */ @@ -1172,10 +1270,10 @@ private JScrollPane getPermissionsScroll() { } return permissionScroll; } - + /** * This method initializes permissionListBox - * + * * @return edu.csus.ecs.pc2.ui.JCheckBoxJList */ private JCheckBoxJList getPermissionsListBox() { @@ -1184,11 +1282,11 @@ private JCheckBoxJList getPermissionsListBox() { } return permissionListBox; } - + /** * This method initializes siteSCroll - * + * * @return javax.swing.JScrollPane */ private JScrollPane getSitesScroll() { @@ -1201,7 +1299,7 @@ private JScrollPane getSitesScroll() { /** * This method initializes siteListBox - * + * * @return edu.csus.ecs.pc2.ui.JCheckBoxJList */ private JCheckBoxJList getSiteListBox() { diff --git a/src/edu/csus/ecs/pc2/ui/ICPCAccountPane.java b/src/edu/csus/ecs/pc2/ui/ICPCAccountPane.java index a103d92d1..b599f231b 100644 --- a/src/edu/csus/ecs/pc2/ui/ICPCAccountPane.java +++ b/src/edu/csus/ecs/pc2/ui/ICPCAccountPane.java @@ -1,15 +1,27 @@ -// 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.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.HashSet; import java.util.Hashtable; import java.util.Vector; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import com.ibm.webrunner.j2mclb.util.HeapSorter; +import com.ibm.webrunner.j2mclb.util.NumericStringComparator; import edu.csus.ecs.pc2.core.IInternalController; import edu.csus.ecs.pc2.core.imports.ICPCAccount; @@ -18,21 +30,10 @@ import edu.csus.ecs.pc2.core.model.AccountEvent; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.IAccountListener; import edu.csus.ecs.pc2.core.model.IInternalContest; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JButton; -import javax.swing.SwingUtilities; - -import com.ibm.webrunner.j2mclb.util.HeapSorter; -import com.ibm.webrunner.j2mclb.util.NumericStringComparator; - -import java.awt.FlowLayout; -import javax.swing.JCheckBox; -import java.awt.Dimension; - /** * @author pc2@ecs.csus.edu * @version $Id$ @@ -42,7 +43,7 @@ public class ICPCAccountPane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = -94109299290020550L; @@ -57,46 +58,52 @@ public class ICPCAccountPane extends JPanePlugin { private JButton cancelButton = null; private ICPCAccount[] icpcAccounts; - + private String displayChoice = "SHORTSCHOOLNAME"; - + private Vector updatedAccountVector = new Vector(); // @jve:decl-index=0: - private JCheckBox showAllCheckBox = null; + private JCheckBox showAllCheckBox = null; /** * @author pc2@ecs.csus.edu * */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { changeDisplayName(displayChoice); } + @Override public void accountModified(AccountEvent event) { changeDisplayName(displayChoice); } + @Override public void accountsAdded(AccountEvent accountEvent) { changeDisplayName(displayChoice); } + @Override public void accountsModified(AccountEvent accountEvent) { changeDisplayName(displayChoice); } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { changeDisplayName(displayChoice); } } /** - * + * * @author pc2@ecs.csus.edu - * + * */ public class PropertyChangeListenerImplementation implements PropertyChangeListener { + @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getNewValue() != null && !evt.getNewValue().equals(evt.getOldValue())) { // display choice has changed @@ -108,7 +115,7 @@ public void propertyChange(PropertyChangeEvent evt) { } /** - * + * */ public ICPCAccountPane() { super(); @@ -147,20 +154,20 @@ private Object[] buildAccountRow(Account account, String newDisplayName) { } return null; } - + protected void changeDisplayName(String string) { if (string != null && string.length() > 0) { updatedAccountVector.clear(); getAccountListBox().removeAllRows(); String newDisplayName; - + Hashtable icpcAccountsHash = new Hashtable(); for (ICPCAccount account : icpcAccounts) { if (account.getClientId() != null) { icpcAccountsHash.put(account.getClientId(), account); } } - + // update all the accounts, if they have the icpc data // note, the icpcAccount (if available) will be merged in updateDisplayName for(Account account : getContest().getAccounts(ClientType.Type.TEAM)) { @@ -208,7 +215,7 @@ protected void changeDisplayName(String string) { updateDisplayName(account, icpcAccount, newDisplayName); } // else skip this account, no icpc data } - + if (updatedAccountVector.size() > 0) { cancelButton.setText("Cancel"); } else { @@ -223,7 +230,7 @@ protected void changeDisplayName(String string) { * This method will update the Account if the newDisplayName does not * equal the account2.getDisplayName(). If an update is required * the Account will be added to the updateAccountVector. - * + * * @param account2 * @param newDisplayName */ @@ -233,7 +240,14 @@ private void updateDisplayName(Account account2, ICPCAccount icpcAccount, String // a deep clone of the string fields Account account = new Account(account2.getClientId(), account2.getPassword(), account2.getClientId().getSiteNumber()); account.clearListAndLoadPermissions(account2.getPermissionList()); - account.setGroupId(account2.getGroupId()); + + // Copy groups to new account + HashSet groupsClean = account2.getGroupIds(); + if(groupsClean != null) { + for(ElementId elementId : groupsClean) { + account.addGroupId(elementId, account2.getPrimaryGroupId() == elementId); + } + } account.setAliasName(new String(account2.getAliasName())); account.setDisplayName(new String(account2.getDisplayName())); account.setExternalId(new String(account2.getExternalId())); @@ -246,7 +260,15 @@ private void updateDisplayName(Account account2, ICPCAccount icpcAccount, String account.setExternalName(icpcAccount.getExternalName()); account.setLongSchoolName(icpcAccount.getLongSchoolName()); account.setShortSchoolName(icpcAccount.getShortSchoolName()); - account.setGroupId(icpcAccount.getGroupId()); + + HashMap groups = icpcAccount.getGroups(); + if(groups != null) { + for(ElementId groupElementId : groups.keySet()) { + // TODO: when/if CMS supports multiple group ID's, + // then 'true' should only be used for the primary group (main) + account.addGroupId(groupElementId, true); + } + } } String oldDisplayName = account.getDisplayName(); account.setDisplayName(newDisplayName); @@ -262,7 +284,7 @@ private void updateDisplayName(Account account2, ICPCAccount icpcAccount, String /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); @@ -280,7 +302,7 @@ public String getPluginTitle() { /** * This method initializes displayNameFormatterPane - * + * * @return edu.csus.ecs.pc2.ui.DisplayNameFormatterPane */ private DisplayNameFormatterPane getDisplayNameFormatterPane() { @@ -294,7 +316,7 @@ private DisplayNameFormatterPane getDisplayNameFormatterPane() { /** * This method initializes accountListbox - * + * * @return com.ibm.webrunner.j2mclb.MultiColumnListbox */ private MCLB getAccountListBox() { @@ -319,7 +341,7 @@ private MCLB getAccountListBox() { // Display Name accountListBox.setColumnSorter(3, sorter, 4); - + // new Display Name accountListBox.setColumnSorter(4, sorter, 5); @@ -333,7 +355,7 @@ private MCLB getAccountListBox() { /** * This method initializes buttonPanel - * + * * @return javax.swing.JPanel */ private JPanel getButtonPanel() { @@ -351,7 +373,7 @@ private JPanel getButtonPanel() { /** * This method initializes updateButton - * + * * @return javax.swing.JButton */ private JButton getUpdateButton() { @@ -361,6 +383,7 @@ private JButton getUpdateButton() { // let changeDisplayName() changes to enabled as needed updateButton.setEnabled(false); updateButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { handleUpdateButton(); } @@ -383,7 +406,7 @@ protected void handleUpdateButton() { /** * This method initializes cancelButton - * + * * @return javax.swing.JButton */ private JButton getCancelButton() { @@ -391,6 +414,7 @@ private JButton getCancelButton() { cancelButton = new JButton(); cancelButton.setText("Cancel"); cancelButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { handleCancelButton(); } @@ -401,9 +425,11 @@ public void actionPerformed(java.awt.event.ActionEvent e) { private void addWindowCloserListener() { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { if (getParentFrame() != null) { getParentFrame().addWindowListener(new java.awt.event.WindowAdapter() { + @Override public void windowClosing(java.awt.event.WindowEvent e) { handleCancelButton(); } @@ -413,6 +439,7 @@ public void windowClosing(java.awt.event.WindowEvent e) { }); } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); addWindowCloserListener(); @@ -423,9 +450,9 @@ public void setContestAndController(IInternalContest inContest, IInternalControl protected void handleCancelButton() { if (updatedAccountVector.size() > 0) { // Something changed, are they sure ? - + int result = FrameUtilities.yesNoCancelDialog(getParentFrame(), "Accounts modified, save changes?", "Confirm Choice"); - + if (result == JOptionPane.YES_OPTION) { // pretend that hit update in that case handleUpdateButton(); @@ -450,7 +477,7 @@ public void setIcpcAccounts(ICPCAccount[] icpcAccounts) { /** * This method initializes showAllCheckBox - * + * * @return javax.swing.JCheckBox */ private JCheckBox getShowAllCheckBox() { @@ -459,6 +486,7 @@ private JCheckBox getShowAllCheckBox() { showAllCheckBox.setText("Included unchanged accounts"); showAllCheckBox.setPreferredSize(new Dimension(250, 24)); showAllCheckBox.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { changeDisplayName(displayChoice); } diff --git a/src/edu/csus/ecs/pc2/ui/ICPCLoadPane.java b/src/edu/csus/ecs/pc2/ui/ICPCLoadPane.java index e0c8f6c34..e176e2667 100644 --- a/src/edu/csus/ecs/pc2/ui/ICPCLoadPane.java +++ b/src/edu/csus/ecs/pc2/ui/ICPCLoadPane.java @@ -1,4 +1,4 @@ -// 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.Component; @@ -34,7 +34,7 @@ /** * ICPC CMS Import Pane. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -43,7 +43,7 @@ public class ICPCLoadPane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = 4668437987656812366L; @@ -70,7 +70,7 @@ public class ICPCLoadPane extends JPanePlugin { /** * This method initializes - * + * */ public ICPCLoadPane() { super(); @@ -79,7 +79,7 @@ public ICPCLoadPane() { /** * This method initializes this - * + * */ private void initialize() { FlowLayout flowLayout = new FlowLayout(); @@ -98,6 +98,7 @@ public String getPluginTitle() { return "ICPC Import Data Pane"; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); @@ -108,6 +109,7 @@ public void setContestAndController(IInternalContest inContest, IInternalControl initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { // wait for groups before enabling accounts getImportAccountsButton().setEnabled(getContest().getGroups() != null); @@ -125,7 +127,7 @@ private void updateGUIperPermissions() { /** * This method initializes importAccountsButton - * + * * @return javax.swing.JButton */ private JButton getImportAccountsButton() { @@ -134,6 +136,7 @@ private JButton getImportAccountsButton() { importAccountsButton.setText("Import CMS team tab files"); importAccountsButton.setToolTipText("Load ICPC CMS PC2_Team.tab file"); importAccountsButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { // load the accounts loadPC2Team(); @@ -184,7 +187,7 @@ protected void loadPC2Team() { /** * This method initializes changeDisplayFormatButton - * + * * @return javax.swing.JButton */ private JButton getChangeDisplayFormatButton() { @@ -192,6 +195,7 @@ private JButton getChangeDisplayFormatButton() { changeDisplayFormatButton = new JButton(); changeDisplayFormatButton.setText("Change Display Format"); changeDisplayFormatButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { changeDisplayFormat(); } @@ -213,7 +217,7 @@ protected void changeDisplayFormat() { } } } catch (Exception e) { - log.throwing("ICPCPane", "changeDisplayFormat", e); + log.throwing("ICPCLoadPane", "changeDisplayFormat", e); } if (importData == null && !gotData) { JOptionPane.showMessageDialog(this, "Please 'Import CMS team tab files' icpc account data first.", "Error", JOptionPane.ERROR_MESSAGE); @@ -232,7 +236,7 @@ private boolean isEmpty(String longSchoolName) { /** * This method initializes importSitesButton - * + * * @return javax.swing.JButton */ private JButton getImportSitesButton() { @@ -243,6 +247,7 @@ private JButton getImportSitesButton() { importSitesButton.setToolTipText("Load CMS PC2_Site.tab and PC2_Contest.tab"); importSitesButton.setMnemonic(KeyEvent.VK_S); importSitesButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { loadPC2Site(); } @@ -273,7 +278,7 @@ protected void loadPC2Site() { addGroups(); - + } // canRead } // isFile } // exists @@ -290,13 +295,13 @@ protected void loadPC2Site() { private void addGroups() throws Exception { // TODO CLEANUP move this off the swing thread, maybe into its own class - + ICPCImportData importSiteData = LoadICPCData.loadSites(lastDir, getContest().getSites()); Group[] importedGroups = importSiteData.getGroups(); Group[] modelGroups = getContest().getGroups(); - + // TODO CLEANUP this is a funky location, but we do not want to add a 3rd icpc load for it - + String contestTitle = importSiteData.getContestTitle(); if (contestTitle != null && contestTitle.trim().length() > 0) { ContestInformation ci = getContest().getContestInformation(); @@ -317,7 +322,7 @@ private void addGroups() throws Exception { groupMap.put(group.getDisplayName(), group); groupMap.put(Integer.toString(group.getGroupId()), group); } - + for (Group group : importedGroups) { if (groupMap.containsKey(Integer.toString(group.getGroupId()))) { mergeGroups(groupMap.get(Integer.toString(group.getGroupId())), group); @@ -331,16 +336,16 @@ private void addGroups() throws Exception { } } } - } + } // else // TODO CLEANUP odd, but is it an error if we have no groups? // Yes it is an error if there are no groups. - + } /** * This method merges the data read from PC2_Site.tab into the model. - * + * * @param destGroup * @param srcGroup */ @@ -359,7 +364,7 @@ private void mergeGroups(Group dstGroup, Group srcGroup) { /** * This method initializes importTSVButton - * + * * @return javax.swing.JButton */ private JButton getImportTSVButton() { @@ -368,6 +373,7 @@ private JButton getImportTSVButton() { importTSVButton.setText("Import CMS tsv files"); importTSVButton.setToolTipText("Load ICPC CMS contest.tsv, teams.tsv and groups.tsv"); importTSVButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { loadTSVFiles(); } @@ -375,8 +381,8 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } return importTSVButton; } - - + + public File selectTSVFileDialog(Component parent, String startDirectory) { JFileChooser chooser = new JFileChooser(startDirectory); @@ -385,11 +391,11 @@ public File selectTSVFileDialog(Component parent, String startDirectory) { FileFilter filterYAML = new FileNameExtensionFilter( "TSV document (*.tsv)", "tsv"); chooser.addChoosableFileFilter(filterYAML); - + chooser.setAcceptAllFileFilterUsed(false); // bug 759 java7 requires us to select it, otherwise the default choice would be empty chooser.setFileFilter(filterYAML); - + int action = chooser.showOpenDialog(parent); switch (action) { @@ -420,19 +426,19 @@ private String selectFileName() throws IOException { /** * Load CCS TSV CMS files. - * + * */ protected void loadTSVFiles() { - + LoadICPCTSVData loader = new LoadICPCTSVData(); loader.setContestAndController(getContest(), getController()); - + String filename = null; try { filename = selectFileName(); - + boolean loaded = loader.loadFiles(filename); - + if (loaded){ info ("Loaded data from file "+filename); } @@ -457,16 +463,17 @@ private void info(String message) { /** * Account Listener Implementation. - * + * * @author pc2@ecs.csus.edu - * @version $Id: ICPCPane.java 2275 2010-11-30 03:39:24Z laned $ */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { // ignored } + @Override public void accountModified(AccountEvent accountEvent) { // check if is this account Account account = accountEvent.getAccount(); @@ -477,6 +484,7 @@ public void accountModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -485,10 +493,12 @@ public void run() { } } + @Override public void accountsAdded(AccountEvent accountEvent) { // ignore } + @Override public void accountsModified(AccountEvent accountEvent) { Account[] accounts = accountEvent.getAccounts(); for (Account account : accounts) { @@ -500,6 +510,7 @@ public void accountsModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -508,11 +519,13 @@ public void run() { } } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -522,26 +535,29 @@ public void run() { /** * Group Listener for ICPC Pane. - * + * * @author pc2@ecs.csus.edu - * @version $Id: ICPCPane.java 2275 2010-11-30 03:39:24Z laned $ */ - // $HeadURL: http://pc2.ecs.csus.edu/repos/pc2v9/trunk/src/edu/csus/ecs/pc2/ui/ICPCPane.java $ + // $HeadURL: http://pc2.ecs.csus.edu/repos/pc2v9/trunk/src/edu/csus/ecs/pc2/ui/ICPCLoadPane.java $ public class GroupListenerImplementation implements IGroupListener { + @Override public void groupAdded(GroupEvent event) { getImportAccountsButton().setEnabled(getContest().getGroups() != null); } + @Override public void groupChanged(GroupEvent event) { getImportAccountsButton().setEnabled(getContest().getGroups() != null); } + @Override public void groupRemoved(GroupEvent event) { // ignore } + @Override public void groupRefreshAll(GroupEvent groupEvent) { getImportAccountsButton().setEnabled(getContest().getGroups() != null); } @@ -559,7 +575,7 @@ public void groupsChanged(GroupEvent event) { /** * This method initializes ICPCAccountFrame - * + * * @return edu.csus.ecs.pc2.ui.ICPCAccountFrame */ private ICPCAccountFrame getICPCAccountFrame() { @@ -568,8 +584,8 @@ private ICPCAccountFrame getICPCAccountFrame() { } return icpcAccountFrame; } - - + + // private JButton getEditCDPPathButton() { // if (editCDPPathButton == null) { // editCDPPathButton = new JButton("Set CDP Path"); @@ -594,7 +610,7 @@ private ICPCAccountFrame getICPCAccountFrame() { // } // /** Returns a singleton instance of the Frame used to edit the CDP path(s). -// * +// * // * @return the EditCDPPathFrame // */ // private EditJudgesDataFilePathFrame getEditCDPFrame() { @@ -603,6 +619,6 @@ private ICPCAccountFrame getICPCAccountFrame() { // } // return editCDPPathFrame ; // } - - + + } // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/ICPCPane.java b/src/edu/csus/ecs/pc2/ui/ICPCPane.java deleted file mode 100644 index 5340e9349..000000000 --- a/src/edu/csus/ecs/pc2/ui/ICPCPane.java +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. -package edu.csus.ecs.pc2.ui; - -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.KeyEvent; -import java.io.File; -import java.util.HashMap; -import java.util.Vector; - -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; - -import edu.csus.ecs.pc2.core.IInternalController; -import edu.csus.ecs.pc2.core.imports.ICPCImportData; -import edu.csus.ecs.pc2.core.imports.LoadICPCData; -import edu.csus.ecs.pc2.core.log.Log; -import edu.csus.ecs.pc2.core.model.Account; -import edu.csus.ecs.pc2.core.model.AccountEvent; -import edu.csus.ecs.pc2.core.model.ClientType; -import edu.csus.ecs.pc2.core.model.ContestInformation; -import edu.csus.ecs.pc2.core.model.Group; -import edu.csus.ecs.pc2.core.model.GroupEvent; -import edu.csus.ecs.pc2.core.model.IAccountListener; -import edu.csus.ecs.pc2.core.model.IGroupListener; -import edu.csus.ecs.pc2.core.model.IInternalContest; -import edu.csus.ecs.pc2.core.security.Permission; - -/** - * ICPC Load CSV files. - * - * This has been deprecated, unable to add TSV button in VE, re-did/moved - * this class to {@link ICPCLoadPane}. - * - * This Pane has 2 buttons, one for loading the ICPC tab files for PC^2 and a 2nd button - * just to change the display name format. - * - * @author pc2@ecs.csus.edu - * @version $Id$ - * @deprecated Use {@link ICPCLoadPane}. - */ -// $HeadURL$ -public class ICPCPane extends JPanePlugin { - - /** - * - */ - private static final long serialVersionUID = 5514087353704960972L; - - private JButton changeDisplayFormatButton = null; - - private JButton importAccountsButton = null; - - private ICPCAccountFrame icpcAccountFrame = null; - - private ICPCImportData importData; - - private String lastDir; - - private Log log; - - private JButton importSitesButton = null; - - /** - * Group Listener for ICPC Pane. - * @author pc2@ecs.csus.edu - * @version $Id$ - */ - - // $HeadURL$ - public class GroupListenerImplementation implements IGroupListener { - - public void groupAdded(GroupEvent event) { - getImportAccountsButton().setEnabled(getContest().getGroups() != null); - } - - public void groupChanged(GroupEvent event) { - getImportAccountsButton().setEnabled(getContest().getGroups() != null); - } - - public void groupRemoved(GroupEvent event) { - // TODO Auto-generated method stub - } - - public void groupRefreshAll(GroupEvent groupEvent) { - getImportAccountsButton().setEnabled(getContest().getGroups() != null); - } - - @Override - public void groupsAdded(GroupEvent event) { - getImportAccountsButton().setEnabled(getContest().getGroups() != null); - } - - @Override - public void groupsChanged(GroupEvent event) { - getImportAccountsButton().setEnabled(getContest().getGroups() != null); - } - } - - - /** - * This method initializes - * - */ - public ICPCPane() { - super(); - initialize(); - } - - /** - * This method initializes this - * - */ - private void initialize() { - FlowLayout flowLayout = new FlowLayout(); - flowLayout.setHgap(50); - this.setLayout(flowLayout); - this.setSize(new java.awt.Dimension(448, 207)); - this.add(getImportSitesButton(), null); - this.add(getImportAccountsButton(), null); - this.add(getChangeDisplayFormatButton(), null); - - } - - /* - * (non-Javadoc) - * - * @see edu.csus.ecs.pc2.ui.JPanePlugin#getPluginTitle() - */ - @Override - public String getPluginTitle() { - return "ICPC Data Pane"; - } - - /** - * This method initializes importAccountsButton - * - * @return javax.swing.JButton - */ - private JButton getImportAccountsButton() { - if (importAccountsButton == null) { - importAccountsButton = new JButton(); - importAccountsButton.setText("Import Accounts"); - importAccountsButton.setPreferredSize(new java.awt.Dimension(150, 26)); - importAccountsButton.setMnemonic(KeyEvent.VK_A); - importAccountsButton.setEnabled(false); - importAccountsButton.setToolTipText("Import PC^2 ICPC contest initialization data (PC2_Team.tab)"); - importAccountsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { - // load the accounts - loadPC2Team(); - } - }); - } - return importAccountsButton; - } - - /** - * This method initializes ICPCAccountFrame - * - * @return edu.csus.ecs.pc2.ui.ICPCAccountFrame - */ - private ICPCAccountFrame getICPCAccountFrame() { - if (icpcAccountFrame == null) { - icpcAccountFrame = new ICPCAccountFrame(); - } - return icpcAccountFrame; - } - - protected void loadPC2Site() { - try { - JFileChooser chooser = new JFileChooser(lastDir); - chooser.setDialogTitle("Select PC2_Site.tab"); - chooser.setFileFilter(new TabFileFilter()); - int returnVal = chooser.showOpenDialog(this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File newFile = chooser.getSelectedFile().getCanonicalFile(); - boolean newFileProblem = true; - if (newFile.exists()) { - if (newFile.isFile()) { - if (newFile.canRead()) { - // update lastDir otherwise it is null - lastDir = chooser.getCurrentDirectory().toString(); - - // TODO move this off the swing thread, maybe into its own class - ICPCImportData importSiteData = LoadICPCData.loadSites(lastDir, getContest().getSites()); - newFileProblem = false; - Group[] importedGroups = importSiteData.getGroups(); - Group[] modelGroups = getContest().getGroups(); - // XXX this is a funky location, but we do not want to add a 3rd icpc load for it - String contestTitle = importSiteData.getContestTitle(); - if (contestTitle != null && contestTitle.trim().length() > 0) { - ContestInformation ci = getContest().getContestInformation(); - ci.setContestTitle(contestTitle); - getController().updateContestInformation(ci); - } - if (importedGroups != null && importedGroups.length > 0) { - if (modelGroups == null || modelGroups.length == 0) { - for (Group group : importedGroups) { - getController().addNewGroup(group); - } - } else { - // there exists modelGroups, that we need to merge with - // primary match should be based on external id - // secondary match based on name - HashMap groupMap = new HashMap(); - for (Group group : modelGroups) { - groupMap.put(group.getDisplayName(), group); - groupMap.put(Integer.toString(group.getGroupId()), group); - } - for (Group group : importedGroups) { - if (groupMap.containsKey(Integer.toString(group.getGroupId()))) { - mergeGroups(groupMap.get(Integer.toString(group.getGroupId())), group); - } else { - if (groupMap.containsKey(group.getDisplayName())) { - mergeGroups(groupMap.get(group.getDisplayName()), group); - } else { - // new group - getController().addNewGroup(group); - } - } - } - } - } // XXX odd, but is it an error if we have no groups? - } // canRead - } // isFile - } // exists - if (newFileProblem) { - log.warning("Problem reading PC2_Contest.tab " + newFile.getCanonicalPath() + ""); - JOptionPane.showMessageDialog(getParentFrame(), "Could not open file " + newFile, "Warning", JOptionPane.WARNING_MESSAGE); - } - } // APPROVE_ACTION - } catch(Exception e) { - log.log(Log.WARNING, "loadPC2Site exception ", e); - } - } - - protected void loadPC2Team() { - try { - JFileChooser chooser = new JFileChooser(lastDir); - chooser.setDialogTitle("Select PC2_Team.tab"); - chooser.setFileFilter(new TabFileFilter()); - int returnVal = chooser.showOpenDialog(this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File newFile = chooser.getSelectedFile().getCanonicalFile(); - boolean newFileProblem = true; - if (newFile.exists()) { - if (newFile.isFile()) { - if (newFile.canRead()) { - lastDir = chooser.getCurrentDirectory().toString(); - - Account[] accounts; - Vector accountVector = getContest().getAccounts(ClientType.Type.TEAM); - accounts = accountVector.toArray(new Account[accountVector.size()]); - importData = LoadICPCData.loadAccounts(lastDir, getContest().getGroups(), accounts); - newFileProblem = false; - changeDisplayFormat(); - } - } - } - if (newFileProblem) { - log.warning("Problem reading _PC2_Team.tab " + newFile.getCanonicalPath() + ""); - JOptionPane.showMessageDialog(getParentFrame(), "Could not open file " + newFile, "Warning", JOptionPane.WARNING_MESSAGE); - } - } - - } catch (Exception e) { - log.log(Log.WARNING, "Exception ", e); - } - } - - /** - * This method initializes changeDisplayFormatButton - * - * @return javax.swing.JButton - */ - private JButton getChangeDisplayFormatButton() { - if (changeDisplayFormatButton == null) { - changeDisplayFormatButton = new JButton(); - changeDisplayFormatButton.setText("Change Display Format"); - changeDisplayFormatButton.setToolTipText("Change Name Display Choice"); - changeDisplayFormatButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { - changeDisplayFormat(); - } - }); - } - return changeDisplayFormatButton; - } - - protected void changeDisplayFormat() { - boolean gotData = false; - try { - Vector teams = getContest().getAccounts(ClientType.Type.TEAM); - for (Account account : teams) { - if(account != null) { - if (account.getLongSchoolName() != null && !isEmpty(account.getLongSchoolName())) { - gotData = true; - break; - } - } - } - } catch (Exception e) { - log.throwing("ICPCPane", "changeDisplayFormat", e); - } - if (importData == null && !gotData) { - JOptionPane.showMessageDialog(this, "Please 'Import Accounts' icpc account data first.", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - if (importData != null && importData.getAccounts() != null) { - getICPCAccountFrame().setICPCAccounts(importData.getAccounts()); - } - getICPCAccountFrame().setContestAndController(getContest(), getController()); - getICPCAccountFrame().setVisible(true); - } - - private boolean isEmpty(String longSchoolName) { - return longSchoolName == null || longSchoolName.trim().length() == 0; - } - - /** - * This method merges the data read from PC2_Site.tab into the model. - * - * @param destGroup - * @param srcGroup - */ - private void mergeGroups(Group dstGroup, Group srcGroup) { - // no-op if the groups are the same - if (dstGroup.isSameAs(srcGroup)) { - return; - } - dstGroup.setDisplayName(srcGroup.getDisplayName()); - dstGroup.setGroupId(srcGroup.getGroupId()); - if (srcGroup.getSite() != null) { // do not overwrite this - dstGroup.setSite(srcGroup.getSite()); - } - getController().updateGroup(dstGroup); - } - - public void setContestAndController(IInternalContest inContest, IInternalController inController) { - super.setContestAndController(inContest, inController); - - log = getController().getLog(); - getContest().addAccountListener(new AccountListenerImplementation()); - getContest().addGroupListener(new GroupListenerImplementation()); - - initializePermissions(); - - SwingUtilities.invokeLater(new Runnable() { - public void run() { - // wait for groups before enabling accounts - getImportAccountsButton().setEnabled(getContest().getGroups() != null); - updateGUIperPermissions(); - } - }); - } - - private void updateGUIperPermissions() { - changeDisplayFormatButton.setEnabled(isAllowed(Permission.Type.EDIT_ACCOUNT)); - importSitesButton.setEnabled(isAllowed(Permission.Type.EDIT_ACCOUNT)); - importAccountsButton.setEnabled(isAllowed(Permission.Type.EDIT_ACCOUNT)); - } - - /** - * This method initializes importSitesButton - * - * @return javax.swing.JButton - */ - private JButton getImportSitesButton() { - if (importSitesButton == null) { - importSitesButton = new JButton(); - importSitesButton.setPreferredSize(new Dimension(150, 26)); - importSitesButton.setMnemonic(KeyEvent.VK_S); - importSitesButton.setText("Import Sites"); - importSitesButton.setActionCommand("Import Sites"); - importSitesButton.setToolTipText("Import PC^2 ICPC contest initialization data (PC2_Site.tab)"); - importSitesButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { - loadPC2Site(); - } - }); - } - return importSitesButton; - } - - /** - * Account Listener Implementation. - * - * @author pc2@ecs.csus.edu - * @version $Id$ - */ - public class AccountListenerImplementation implements IAccountListener { - - public void accountAdded(AccountEvent accountEvent) { - // ignored - } - - public void accountModified(AccountEvent accountEvent) { - // check if is this account - Account account = accountEvent.getAccount(); - /** - * If this is the account then update the GUI display per the potential change in Permissions. - */ - if (getContest().getClientId().equals(account.getClientId())) { - // They modified us!! - initializePermissions(); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - updateGUIperPermissions(); - } - }); - - } - } - - public void accountsAdded(AccountEvent accountEvent) { - // ignore - } - - public void accountsModified(AccountEvent accountEvent) { - Account[] accounts = accountEvent.getAccounts(); - for (Account account : accounts) { - - /** - * If this is the account then update the GUI display per the potential change in Permissions. - */ - if (getContest().getClientId().equals(account.getClientId())) { - // They modified us!! - initializePermissions(); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - updateGUIperPermissions(); - } - }); - } - } - } - - public void accountsRefreshAll(AccountEvent accountEvent) { - - initializePermissions(); - - SwingUtilities.invokeLater(new Runnable() { - public void run() { - updateGUIperPermissions(); - } - }); - } - } - - -} // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/ReportPane.java b/src/edu/csus/ecs/pc2/ui/ReportPane.java index e6e6e6b02..cc3a7d550 100644 --- a/src/edu/csus/ecs/pc2/ui/ReportPane.java +++ b/src/edu/csus/ecs/pc2/ui/ReportPane.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 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.BorderLayout; @@ -119,14 +119,14 @@ /** * Report Pane, allows picking and viewing reports. - * + * * @author pc2@ecs.csus.edu */ public class ReportPane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = -5165297328068331675L; @@ -180,7 +180,7 @@ public String getReportDirectory() { /** * This method can change the directory that the reports will be written to. The default is "reports". - * + * * @param reportDirectory * what directory to write the reports to */ @@ -190,7 +190,7 @@ public void setReportDirectory(String reportDirectory) { /** * This method initializes - * + * */ public ReportPane() { super(); @@ -199,7 +199,7 @@ public ReportPane() { /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); @@ -303,38 +303,41 @@ private void populateReports() { reports.add(new JudgingAnalysisReport()); reports.add(new JSON2016Report()); - + reports.add(new ProblemsGroupReport()); - + reports.add(new ProblemGroupAssignmentReport()); - + reports.add(new ContestCompareReport()); - + reports.add(new CLICSAwardsReport()); - + if (isServer()){ - // SOMEDAY Bug 1166 remove this isServer when added to Admin. + // SOMEDAY Bug 1166 remove this isServer when added to Admin. reports.add(new CDPReport()); } - + reports.add(new EventFeedJSONReport()); - + reports.add(new StandingsNSAReport()); - + reports.add(new ResultsCompareReport()); - + reports.add(new ResultsExportReport()); - + reports.add(new ResultsTSVReport()); - listOfReports = (IReport[]) reports.toArray(new IReport[reports.size()]); - + listOfReports = reports.toArray(new IReport[reports.size()]); + Arrays.sort(listOfReports,new ReportComparator()); } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); + filter.setContestAndController(inContest, inController); + this.log = getController().getLog(); if (isServer()) { @@ -347,6 +350,7 @@ public void setContestAndController(IInternalContest inContest, IInternalControl initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { getEditFilterFrame().setContestAndController(getContest(), getController()); populateReports(); @@ -365,6 +369,7 @@ private void updateGUIperPermissions() { protected void refreshGUI() { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { refreshReportComboBox(); showXMLCheckbox(); @@ -375,7 +380,7 @@ public void run() { private void refreshReportComboBox() { getReportsComboBox().removeAllItems(); - + Arrays.sort(listOfReports,new ReportComparator()); for (IReport report : listOfReports) { @@ -393,7 +398,7 @@ public String getPluginTitle() { /** * This method initializes topPane - * + * * @return javax.swing.JPanel */ private JPanel getTopPane() { @@ -411,7 +416,7 @@ private JPanel getTopPane() { /** * This method initializes buttonPane - * + * * @return javax.swing.JPanel */ private JPanel getButtonPane() { @@ -430,7 +435,7 @@ private JPanel getButtonPane() { /** * This method initializes mainPane - * + * * @return javax.swing.JPanel */ private JPanel getMainPane() { @@ -448,7 +453,7 @@ private JPanel getMainPane() { /** * This method initializes viewReportButton - * + * * @return javax.swing.JButton */ private JButton getViewReportButton() { @@ -458,6 +463,7 @@ private JButton getViewReportButton() { viewReportButton.setToolTipText("View the selected Report"); viewReportButton.setMnemonic(java.awt.event.KeyEvent.VK_V); viewReportButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { if (getBreakdownBySiteCheckbox().isSelected()) { generateSelectedReportBySite(); @@ -547,6 +553,8 @@ public void createReportFile(IReport report, boolean suppressHeaderFooter, Strin PrintWriter printWriter = new PrintWriter(new FileOutputStream(filename, false), true); filter = inFilter; + filter.setContestAndController(getContest(), getController()); + try { if (!suppressHeaderFooter) { @@ -600,11 +608,11 @@ protected void generateSelectedReport(boolean includeHeaderFooter) { if (writeXML) { extension = "xml"; } - + if (selectedReport.getReportTitle().toLowerCase().indexOf("json") != -1) { extension = "json"; } - + String filename = getFileName(selectedReport, extension); File reportDirectoryFile = new File(getReportDirectory()); @@ -633,7 +641,7 @@ protected void generateSelectedReport(boolean includeHeaderFooter) { IReportFile reportFile = (IReportFile) selectedReport; suppressHeaderFooter = reportFile.suppressHeaderFooter(); } - + if (! includeHeaderFooter) { suppressHeaderFooter = true; } @@ -798,12 +806,13 @@ protected void generateSelectedReportBySite() { /** * show message to user - * + * * @param string */ private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); } @@ -813,7 +822,7 @@ public void run() { /** * This method initializes thisSiteCheckBox - * + * * @return javax.swing.JCheckBox */ private JCheckBox getBreakdownBySiteCheckbox() { @@ -824,6 +833,7 @@ private JCheckBox getBreakdownBySiteCheckbox() { breakdownBySiteCheckbox.setToolTipText("Break down by site"); breakdownBySiteCheckbox.setText("Breakdown by site"); breakdownBySiteCheckbox.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { changeSiteFiltering(); } @@ -846,7 +856,7 @@ protected void changeSiteFiltering() { /** * This method initializes reportChoicePane - * + * * @return javax.swing.JPanel */ private JPanel getReportChoicePane() { @@ -863,13 +873,14 @@ private JPanel getReportChoicePane() { /** * This method initializes reportsComboBox - * + * * @return javax.swing.JComboBox */ private JComboBox getReportsComboBox() { if (reportsComboBox == null) { reportsComboBox = new JComboBox(); reportsComboBox.addKeyListener(new java.awt.event.KeyAdapter() { + @Override public void keyPressed(java.awt.event.KeyEvent e) { if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) { if (getBreakdownBySiteCheckbox().isSelected()) { @@ -886,7 +897,7 @@ public void keyPressed(java.awt.event.KeyEvent e) { /** * This method initializes thisClientFilterButton - * + * * @return javax.swing.JCheckBox */ private JCheckBox getThisClientFilterButton() { @@ -897,6 +908,7 @@ private JCheckBox getThisClientFilterButton() { thisClientFilterButton.setText("Filter for this client only"); thisClientFilterButton.setVisible(false); thisClientFilterButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { changeThisClientFiltering(); } @@ -920,7 +932,7 @@ protected void changeThisClientFiltering() { /** * This method initializes filterPane - * + * * @return javax.swing.JPanel */ private JPanel getFilterPane() { @@ -941,7 +953,7 @@ private JPanel getFilterPane() { /** * This method initializes filterButtonPane - * + * * @return javax.swing.JPanel */ private JPanel getFilterButtonPane() { @@ -954,7 +966,7 @@ private JPanel getFilterButtonPane() { /** * This method initializes editReportFilter - * + * * @return javax.swing.JButton */ private JButton getEditReportFilter() { @@ -964,6 +976,7 @@ private JButton getEditReportFilter() { editReportFilter.setMnemonic(KeyEvent.VK_F); editReportFilter.setToolTipText("Edit Filter"); editReportFilter.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { showReportFilter(); } @@ -980,6 +993,7 @@ protected void showReportFilter() { getEditFilterFrame().addList(ListNames.TEAM_ACCOUNTS); getEditFilterFrame().addList(ListNames.RUN_STATES); getEditFilterFrame().addList(ListNames.JUDGEMENTS); + getEditFilterFrame().addList(ListNames.GROUPS); getEditFilterFrame().addList(ListNames.SITES); getEditFilterFrame().setFilter(filter); @@ -991,6 +1005,7 @@ protected void showReportFilter() { public EditFilterFrame getEditFilterFrame() { if (editFilterFrame == null) { Runnable callback = new Runnable() { + @Override public void run() { refreshFilterLabel(); }; @@ -1006,7 +1021,7 @@ private void refreshFilterLabel() { /** * This method initializes xmlOutputCheckbox - * + * * @return javax.swing.JCheckBox */ private JCheckBox getXmlOutputCheckbox() { @@ -1021,7 +1036,7 @@ private JCheckBox getXmlOutputCheckbox() { /** * This method initializes generateSummaryButton - * + * * @return javax.swing.JButton */ private JButton getGenerateSummaryButton() { @@ -1031,6 +1046,7 @@ private JButton getGenerateSummaryButton() { generateSummaryButton.setMnemonic(KeyEvent.VK_G); generateSummaryButton.setToolTipText("Generate Summary Reports"); generateSummaryButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { generateSummaryReport(); } @@ -1054,16 +1070,18 @@ protected void generateSummaryReport() { /** * Account Listener Implementation. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { // ignored } + @Override public void accountModified(AccountEvent accountEvent) { // check if is this account Account account = accountEvent.getAccount(); @@ -1074,6 +1092,7 @@ public void accountModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -1082,10 +1101,12 @@ public void run() { } } + @Override public void accountsAdded(AccountEvent accountEvent) { // ignore } + @Override public void accountsModified(AccountEvent accountEvent) { Account[] accounts = accountEvent.getAccounts(); for (Account account : accounts) { @@ -1097,6 +1118,7 @@ public void accountsModified(AccountEvent accountEvent) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -1105,11 +1127,13 @@ public void run() { } } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); } @@ -1121,7 +1145,8 @@ private JButton getExportDataButton() { if (exportDataButton == null) { exportDataButton = new JButton("Export Report Contents"); exportDataButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + @Override + public void actionPerformed(ActionEvent e) { generateSelectedReport(false); } }); diff --git a/src/edu/csus/ecs/pc2/ui/ReviewAccountLoadFrame.java b/src/edu/csus/ecs/pc2/ui/ReviewAccountLoadFrame.java index 4a0ea812d..2afff653e 100644 --- a/src/edu/csus/ecs/pc2/ui/ReviewAccountLoadFrame.java +++ b/src/edu/csus/ecs/pc2/ui/ReviewAccountLoadFrame.java @@ -3,35 +3,36 @@ import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Dimension; import java.awt.FlowLayout; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Vector; +import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import edu.csus.ecs.pc2.core.IInternalController; +import edu.csus.ecs.pc2.core.imports.LoadAccounts; import edu.csus.ecs.pc2.core.list.AccountComparator; import edu.csus.ecs.pc2.core.log.Log; import edu.csus.ecs.pc2.core.log.StaticLog; import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; +import edu.csus.ecs.pc2.core.model.ElementId; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.Pluralize; -import edu.csus.ecs.pc2.core.imports.LoadAccounts; import edu.csus.ecs.pc2.core.security.Permission; -import javax.swing.JButton; -import javax.swing.JCheckBox; - -import java.awt.Dimension; /** * Review Account Load Frame - * + * * 1st line of load file are the column headers. * Required columns are: account, site, and password * Optional columns are: displayname, group, permdisplay, and permlogin @@ -44,7 +45,7 @@ public class ReviewAccountLoadFrame extends JFrame implements UIPlugin { /** - * + * */ private static final long serialVersionUID = -172535000039944166L; @@ -76,10 +77,10 @@ public class ReviewAccountLoadFrame extends JFrame implements UIPlugin { private String loadedFileName; private JCheckBox showAllAccountsCheckBox = null; - + /** * This method initializes - * + * */ public ReviewAccountLoadFrame() { super(); @@ -88,7 +89,7 @@ public ReviewAccountLoadFrame() { /** * This method initializes this - * + * */ private void initialize() { getContentPane().setLayout(new BorderLayout()); @@ -98,17 +99,18 @@ private void initialize() { this.setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); this.setContentPane(getJPanel()); this.addWindowListener(new java.awt.event.WindowAdapter() { + @Override public void windowClosing(java.awt.event.WindowEvent e) { handleCancel(); } }); - + FrameUtilities.centerFrameTop(this); } /** * This method initializes languageButtonPane - * + * * @return javax.swing.JPanel */ private JPanel getButtonPane() { @@ -127,7 +129,7 @@ private JPanel getButtonPane() { /** * This method initializes languageListBox - * + * * @return edu.csus.ecs.pc2.core.log.MCLB */ private MCLB getAccountListBox() { @@ -161,6 +163,7 @@ private MCLB getAccountListBox() { return accountListBox; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { contest = inContest; controller = inController; @@ -170,7 +173,7 @@ public void setContestAndController(IInternalContest inContest, IInternalControl /** * This method initializes messagePane - * + * * @return javax.swing.JPanel */ private JPanel getMessagePane() { @@ -189,12 +192,13 @@ private JPanel getMessagePane() { /** * show message to user - * + * * @param string */ private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); messageLabel.setToolTipText(string); @@ -207,6 +211,7 @@ public void run() { private void showMessage(final String string, final Color color) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); messageLabel.setToolTipText(string); @@ -214,9 +219,10 @@ public void run() { } }); } - - - + + + + @Override public String getPluginTitle() { return "Review Account Load Frame"; } @@ -224,7 +230,7 @@ public String getPluginTitle() { /** * Return all accounts for all sites. * TODO: consider making getAllAccounts public in controller - * + * * @return Array of all accounts in contest. */ private Account[] getAllAccounts() { @@ -238,12 +244,12 @@ private Account[] getAllAccounts() { } } - Account[] accountList = (Account[]) allAccounts.toArray(new Account[allAccounts.size()]); + Account[] accountList = allAccounts.toArray(new Account[allAccounts.size()]); return accountList; } public void setFile(String filename) { - + loadedFileName = filename; showMessage("Loaded " + filename); getAccountListBox().removeAllRows(); @@ -280,7 +286,7 @@ private void refreshList() { } } } - + private void showErrorMessage(String msg){ showMessage(msg, Color.RED); } @@ -289,6 +295,7 @@ private void showErrorMessage(String msg){ public void updateAccountRow(final Account account) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { Object[] objects = buildAccountRow(account); int rowNumber = accountListBox.getIndexByKey(account.getClientId()); @@ -334,21 +341,34 @@ protected Object[] buildAccountRow(Account account) { perms = perms + "CHANGE_PASSWORD "; } s[5] = perms.trim(); - if (accountOrig.getGroupId() == null && account.getGroupId() == null) { + + HashSet groupsOrig = accountOrig.getGroupIds(); + HashSet groupsNew = account.getGroupIds(); + + if (groupsOrig == null && groupsNew == null) { s[6] = ""; } else { - if (account.getGroupId() == null) { + if (groupsNew == null) { s[6] = CHANGE_BEGIN + "" + CHANGE_END; } else { - if (accountOrig.getGroupId() == null) { - s[6] = CHANGE_BEGIN + contest.getGroup(account.getGroupId()).toString() + CHANGE_END; - } else { - // neither are null - if (account.getGroupId().equals(accountOrig.getGroupId())) { - s[6] = contest.getGroup(account.getGroupId()).toString(); + // we're going to always need the new accounts list of groups, so compute it once first + String allGroups = ""; + boolean firstString = true; + + for(ElementId groupElementId : groupsNew) { + if(!firstString) { + allGroups = allGroups + ','; } else { - s[6] = CHANGE_BEGIN + contest.getGroup(account.getGroupId()).toString() + CHANGE_END; + firstString = false; } + allGroups = allGroups + contest.getGroup(groupElementId).getDisplayName(); + } + + // groupsOrig may be null here, but groupsNew will always be non-null + if (groupsNew.equals(groupsOrig)) { + s[6] = allGroups; + } else { + s[6] = CHANGE_BEGIN + allGroups + CHANGE_END; } } } @@ -414,7 +434,7 @@ private String getTeamDisplayName(Account account) { /** * This method initializes acceptButton - * + * * @return javax.swing.JButton */ private JButton getAcceptButton() { @@ -425,6 +445,7 @@ private JButton getAcceptButton() { acceptButton.setEnabled(false); acceptButton.setPreferredSize(new java.awt.Dimension(100, 26)); acceptButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { handleAccept(); } @@ -441,7 +462,7 @@ protected void handleAccept() { /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getJPanel() { @@ -458,7 +479,7 @@ private JPanel getJPanel() { /** * This method initializes cancelButton - * + * * @return javax.swing.JButton */ private JButton getCancelButton() { @@ -468,6 +489,7 @@ private JButton getCancelButton() { cancelButton.setMnemonic(java.awt.event.KeyEvent.VK_C); cancelButton.setPreferredSize(new java.awt.Dimension(100, 26)); cancelButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { handleCancel(); } @@ -482,7 +504,7 @@ protected void handleCancel() { /** * This method initializes showAllAccountsCheckBox - * + * * @return javax.swing.JCheckBox */ private JCheckBox getShowAllAccountsCheckBox() { @@ -491,6 +513,7 @@ private JCheckBox getShowAllAccountsCheckBox() { showAllAccountsCheckBox.setText("Include unchanged accounts"); showAllAccountsCheckBox.setPreferredSize(new Dimension(250, 24)); showAllAccountsCheckBox.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { refreshList(); } diff --git a/src/edu/csus/ecs/pc2/ui/RunsTablePane.java b/src/edu/csus/ecs/pc2/ui/RunsTablePane.java index bb5d59937..21b166ac6 100644 --- a/src/edu/csus/ecs/pc2/ui/RunsTablePane.java +++ b/src/edu/csus/ecs/pc2/ui/RunsTablePane.java @@ -1,4 +1,4 @@ -// 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.BorderLayout; @@ -21,9 +21,9 @@ import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.RowSorter; +import javax.swing.RowSorter.SortKey; import javax.swing.SortOrder; import javax.swing.SwingUtilities; -import javax.swing.RowSorter.SortKey; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumnModel; @@ -64,8 +64,8 @@ import edu.csus.ecs.pc2.core.model.ProblemEvent; import edu.csus.ecs.pc2.core.model.Run; import edu.csus.ecs.pc2.core.model.Run.RunStates; -import edu.csus.ecs.pc2.core.model.RunEvent.Action; import edu.csus.ecs.pc2.core.model.RunEvent; +import edu.csus.ecs.pc2.core.model.RunEvent.Action; import edu.csus.ecs.pc2.core.model.RunFiles; import edu.csus.ecs.pc2.core.model.RunUtilities; import edu.csus.ecs.pc2.core.model.SerializedFile; @@ -78,16 +78,16 @@ /** * View runs panel. - * + * * @author pc2@ecs.csus.edu */ public class RunsTablePane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = 114647004580210428L; - + private static final int VERT_PAD = 2; private static final int HORZ_PAD = 20; @@ -126,24 +126,24 @@ public class RunsTablePane extends JPanePlugin { private Log log = null; private JLabel rowCountLabel = null; - + private JScrollPane scrollPane = null; /** * Show huh */ private boolean showNewRunsOnly = false; - + /** * Filter that does not change. - * - * Used to do things like insure only New runs or Judged runs + * + * Used to do things like insure only New runs or Judged runs */ private Filter requiredFilter = new Filter(); /** * Show who is judging column and status. - * + * */ private boolean showJudgesInfo = true; @@ -165,17 +165,17 @@ public class RunsTablePane extends JPanePlugin { private boolean makeSoundOnOneRun = false; private boolean bUseAutoJudgemonitor = true; - + private EditFilterFrame editFilterFrame = null; private String filterFrameTitle = "Run filter"; - + private ExtractRuns extractRuns = null; private boolean teamClient = true; - + private JudgementNotificationsList judgementNotificationsList = null; - + private boolean displayConfirmation = true; private JButton viewSourceButton; @@ -184,16 +184,16 @@ public class RunsTablePane extends JPanePlugin { private Run fetchedRun; private RunFiles fetchedRunFiles; - + private Run requestedRun; - + private boolean showSourceActive = false; protected int viewSourceThreadCounter; /** * This method initializes - * + * */ public RunsTablePane() { super(); @@ -212,7 +212,7 @@ public RunsTablePane(boolean useAutoJudgeMonitor) { /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); @@ -289,7 +289,7 @@ protected Object[] buildRunRow(Run run, ClientId judgeId) { /** * Return balloon color if problem solved. - * + * * @param run * @return */ @@ -372,7 +372,7 @@ private boolean isAutoJudgedRun(Run run) { /** * Return the judgement/status for the run. - * + * * @param run * @return a string that represents the state of the run */ @@ -381,7 +381,7 @@ protected String getJudgementResultString(Run run) { String result = ""; if (run.isJudged()) { - + if (run.isSolved()) { result = getContest().getJudgements()[0].getDisplayName(); if (run.getStatus().equals(RunStates.MANUAL_REVIEW)) { @@ -396,12 +396,12 @@ protected String getJudgementResultString(Run run) { } } } - + // only consider changing the state to new if we are a team if (isTeam(getContest().getClientId()) && !run.getJudgementRecord().isSendToTeam()) { result = RunStates.NEW.toString(); } - + } else { result = "No"; @@ -420,7 +420,7 @@ protected String getJudgementResultString(Run run) { if (!isTeam(getContest().getClientId())) { result = RunStates.MANUAL_REVIEW + " (" + result + ")"; } else { - + // Only show to team if (showPreliminaryJudgementToTeam(run)) { result = "PRELIMINARY (" + result + ")"; @@ -429,7 +429,7 @@ protected String getJudgementResultString(Run run) { } } } - + if (isTeam(getContest().getClientId())) { if (!judgementRecord.isSendToTeam()) { result = RunStates.NEW.toString(); @@ -441,11 +441,11 @@ protected String getJudgementResultString(Run run) { } } } - + /** - * + * */ - + if (teamClient || isAllowed(Permission.Type.RESPECT_EOC_SUPPRESSION)){ if (RunUtilities.supppressJudgement(judgementNotificationsList, run, getContest().getContestTime())){ result = RunStates.NEW.toString(); @@ -459,30 +459,30 @@ protected String getJudgementResultString(Run run) { result = RunStates.NEW.toString(); } } - + if (run.isDeleted()) { result = "DEL " + result; } - - + + return result; } private boolean showPreliminaryJudgementToTeam(Run run) { - + try { Problem problem = getContest().getProblem(run.getProblemId()); return problem.isPrelimaryNotification(); } catch (Exception e) { log.log(Log.WARNING, "Exception trying to get Problem ", e); - return false; + return false; } } private boolean isTeam(ClientId clientId) { return clientId == null || clientId.getClientType().equals(Type.TEAM); } - + // private boolean isServer(ClientId clientId) { // return clientId == null || clientId.getClientType().equals(Type.SERVER); // } @@ -535,6 +535,7 @@ private String getTeamDisplayName(Run run) { // $HeadURL$ public class RunListenerImplementation implements IRunListener { + @Override public void runAdded(RunEvent event) { updateRunRow(event.getRun(), event.getWhoModifiedRun(), true); // check if this is a team; if so, pop up a confirmation dialog @@ -542,11 +543,13 @@ public void runAdded(RunEvent event) { showResponseToTeam(event); } } - + + @Override public void refreshRuns(RunEvent event) { reloadRunList(); } + @Override public void runChanged(RunEvent event) { updateRunRow(event.getRun(), event.getWhoModifiedRun(), true); @@ -554,60 +557,60 @@ public void runChanged(RunEvent event) { if (getContest().getClientId().getClientType() == ClientType.Type.TEAM) { showResponseToTeam(event); } - + //code copied from FetchRunService.RunListenerImplementation.runChanged(): - + Action action = event.getAction(); Action details = event.getDetailedAction(); Run aRun = event.getRun(); RunFiles aRunFiles = event.getRunFiles(); String msg = event.getMessage(); - + getController().getLog().log(Log.INFO, "RunsPane.RunListener: Action=" + action + "; DetailedAction=" + details + "; msg=" + msg + "; run=" + aRun + "; runFiles=" + aRunFiles); if (aRun != null) { // make local reference for consistency in case requestedRun gets cleared - avoids synchronization Run locRun = requestedRun; - + // we are only interested in the run we may have requested from View Source - all other run changes are ignored. if(locRun != null && aRun.getNumber() == locRun.getNumber() && aRun.getSiteNumber() == locRun.getSiteNumber()) { // RUN_NOT_AVAILABLE is undirected (sentToClient is null) if (event.getAction().equals(Action.RUN_NOT_AVAILABLE)) { - + getController().getLog().log(Log.WARNING, "Reply from server: requested run not available"); - serverReplied = true; + serverReplied = true; } else { // Only interested in the first reply (we don't want fetchedRun changing once its been set - it really shouldn't) if(fetchedRun == null) { ClientId toClient = event.getSentToClientId() ; ClientId myID = getContest().getClientId(); - + // see if the event was directed to me explicitly, which it should be (see PacketHandler.handleFetchedRun()) // but isn't due to the way updateRun() works. We'll leave this code here in case someday that changes. if (toClient != null && toClient.equals(myID)) { - + getController().getLog().log(Log.INFO, "Reply from server: " + "Run Status=" + event.getAction() + "; run=" + event.getRun() + "; runFiles=" + event.getRunFiles()); - + fetchedRun = aRun; - fetchedRunFiles = aRunFiles; - serverReplied = true; - + fetchedRunFiles = aRunFiles; + serverReplied = true; + } else { - + // The FETCHED_REQUESTED_RUN reply is sent with a a SentToClientID of null as found in // InternalContest.updateRun(). the RunEvent's sentToClientId member is only set when the run // is being checked out for BEING_JUDGED, BEING_RE_JUDGED, CHECKED_OUT, HOLD, otherwise it is // is not set (null), and winds up here in the case of "View Source" but not from a being-judged dialog. - + getController().getLog().log(Log.INFO, "Event not directed to me: sent to " + toClient + " but my ID is " + myID); - + if(toClient == null) { fetchedRun = aRun; - fetchedRunFiles = aRunFiles; - serverReplied = true; + fetchedRunFiles = aRunFiles; + serverReplied = true; } } } @@ -615,7 +618,7 @@ public void runChanged(RunEvent event) { } else { // changed run was not the one we wanted getController().getLog().log(Log.INFO, "Run event not for requested run: " + "Run Status=" + event.getAction() - + "; run=" + aRun + " requested=" + locRun); + + "; run=" + aRun + " requested=" + locRun); } } else { //run from server was null @@ -623,24 +626,25 @@ public void runChanged(RunEvent event) { fetchedRun = null; fetchedRunFiles = null; } - - - + + + } + @Override public void runRemoved(RunEvent event) { updateRunRow(event.getRun(), event.getWhoModifiedRun(), true); } /** * Show the Run Judgement. - * + * * Checks the run in the specified run event and (potentially) displays a results dialog. If the run has been judged, has a valid judgement record, and is the "active" judgement for scoring * purposes, displays a modal MessageDialog to the Team containing the judgement results. This method assumes the caller has already verified this is a TEAM client; failure to do that on the * caller's part will cause other clients to see the Run Response dialog... */ private void showResponseToTeam(RunEvent event) { - + if (! displayConfirmation){ return; } @@ -658,9 +662,9 @@ private void showResponseToTeam(RunEvent event) { // check if there's a legit judgement JudgementRecord judgementRecord = theRun.getJudgementRecord(); if (judgementRecord != null) { - + if (! judgementRecord.isSendToTeam()){ - + /** * Do not show this judgement to the team, the judge indicated * that the team should not be notified. @@ -699,7 +703,7 @@ private void showResponseToTeam(RunEvent event) { if (theRun.getStatus().equals(RunStates.MANUAL_REVIEW)) { displayString += "NOTE: This is a Preliminary Judgement


    "; } - + if (judgeComment != null) { if (judgeComment.length() > 0) { displayString += "Judge's Comment: " + Utilities.forHTML(judgeComment) + "


    "; @@ -734,10 +738,10 @@ private void showResponseToTeam(RunEvent event) { } } } - + /** * This method initializes scrollPane - * + * * @return javax.swing.JScrollPane */ private JScrollPane getScrollPane() { @@ -749,7 +753,7 @@ private JScrollPane getScrollPane() { /** * This method initializes the runTable - * + * * @return JTableCustomized */ private JTableCustomized getRunTable() { @@ -757,6 +761,7 @@ private JTableCustomized getRunTable() { runTable = new JTableCustomized(); runTable.addMouseListener(new MouseAdapter() { + @Override public void mouseClicked(MouseEvent me) { // If double-click see if we can select the run if (me.getClickCount() == 2) { @@ -770,9 +775,10 @@ public void mouseClicked(MouseEvent me) { } return runTable; } - + public void clearAllRuns() { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { if(runTableModel != null) { // All rows are discarded - the TM will notify the Table @@ -790,7 +796,7 @@ private void resetRunsListBoxColumns() { Object[] fullColumnsNoJudge = { "Site", "Team", "Run Id", "Time", "Status", "Problem", "Balloon", "Language", "OS", "ElementID" }; Object[] teamColumns = { "Site", "Run Id", "Problem", "Time", "Status", "Balloon", "Language", "ElementID" }; Object[] columns; - + usingTeamColumns = false; usingFullColumns = false; @@ -818,23 +824,23 @@ public boolean isCellEditable(int row, int col) { // Sorters TableRowSorter trs = new TableRowSorter(runTableModel); - + runTable.setRowSorter(trs); runTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - + ArrayList sortList = new ArrayList(); - + /* * Column headers left justified */ ((DefaultTableCellRenderer)runTable.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT); runTable.setRowHeight(runTable.getRowHeight() + VERT_PAD); - - + + StringToNumberComparator numericStringSorter = new StringToNumberComparator(); AccountNameCaseComparator accountNameSorter = new AccountNameCaseComparator(); - + int idx = 0; if (isTeam(getContest().getClientId())) { @@ -876,11 +882,11 @@ public boolean isCellEditable(int row, int col) { sortList.add(new RowSorter.SortKey(8, SortOrder.ASCENDING)); sortList.add(new RowSorter.SortKey(9, SortOrder.ASCENDING)); sortList.add(new RowSorter.SortKey(10, SortOrder.ASCENDING)); - + } else { // Object[] fullColumnsNoJudge = { "Site", "Team", "Run Id", "Time", "Status", "Problem", "Balloon", "Language", "OS" }; - + // These are in column order - omitted ones are straight string compare trs.setComparator(0, accountNameSorter); trs.setComparator(1, accountNameSorter); @@ -900,16 +906,17 @@ public boolean isCellEditable(int row, int col) { trs.setSortKeys(sortList); resizeColumnWidth(runTable); } - + private void resizeColumnWidth(JTableCustomized table) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { TableColumnAdjuster tca = new TableColumnAdjuster(table, HORZ_PAD); tca.adjustColumns(); } }); } - + /** * Find row that contains the supplied key (in last column) * @param value - unique key - really, the ElementId of run @@ -917,7 +924,7 @@ public void run() { */ private int getRowByKey(Object value) { Object o; - + if(runTableModel != null) { int col = runTableModel.getColumnCount() - 1; for (int i = runTableModel.getRowCount() - 1; i >= 0; --i) { @@ -932,12 +939,13 @@ private int getRowByKey(Object value) { /** * Remove run from grid by removing the data row from the TableModel - * + * * @param run */ private void removeRunRow(final Run run) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { int rowNumber = getRowByKey(run.getElementId()); @@ -973,7 +981,7 @@ public void updateRunRow(final Run run, final ClientId whoModifiedId, final bool return; } } - + if (requiredFilter != null) { if (!requiredFilter.matches(run)) { // if run does not match requiredFilter, be sure to remove it from grid @@ -984,6 +992,7 @@ public void updateRunRow(final Run run, final ClientId whoModifiedId, final bool } SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { ClientId whoJudgedId = whoModifiedId; @@ -1016,7 +1025,7 @@ public void run() { updateRowCount(); resizeColumnWidth(runTable); } - + // if (selectJudgementFrame != null) { //TODO the selectJudgementFrame should be placed above all PC2 windows, not working when dblClicking in Windows OS // } @@ -1027,7 +1036,7 @@ public void run() { public void reloadRunList() { Run[] runs = getContest().getRuns(); - + ContestInformation contestInformation = getContest().getContestInformation(); judgementNotificationsList = contestInformation.getJudgementNotificationsList(); @@ -1036,7 +1045,7 @@ public void reloadRunList() { } // TODO bulk load these records, this is closer only do the count,size,sort at end - + if (filter.isFilterOn()){ getFilterButton().setForeground(Color.BLUE); getFilterButton().setToolTipText("Edit filter - filter ON"); @@ -1075,6 +1084,7 @@ public void reloadRunList() { updateRunRow(run, clientId, false); } SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateRowCount(); resizeColumnWidth(runTable); @@ -1125,6 +1135,7 @@ private void updateGUIperPermissions() { filterButton.setVisible(true); } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); @@ -1136,14 +1147,14 @@ public void setContestAndController(IInternalContest inContest, IInternalControl displayTeamName = new DisplayTeamName(); displayTeamName.setContestAndController(inContest, inController); - + teamClient = isTeam(inContest.getClientId()); - + ContestInformation contestInformation = getContest().getContestInformation(); judgementNotificationsList = contestInformation.getJudgementNotificationsList(); initializePermissions(); - + extractRuns = new ExtractRuns(inContest); getContest().addRunListener(new RunListenerImplementation()); @@ -1152,23 +1163,24 @@ public void setContestAndController(IInternalContest inContest, IInternalControl getContest().addLanguageListener(new LanguageListenerImplementation()); getContest().addContestInformationListener(new ContestInformationListenerImplementation()); getContest().addBalloonSettingsListener(new BalloonSettingsListenerImplementation()); - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { - + editRunFrame.setContestAndController(getContest(), getController()); viewJudgementsFrame.setContestAndController(getContest(), getController()); if (isAllowed(Permission.Type.JUDGE_RUN)) { selectJudgementFrame.setContestAndController(getContest(), getController()); } - + getEditFilterFrame().setContestAndController(getContest(), getController()); - + updateGUIperPermissions(); resetRunsListBoxColumns(); reloadRunList(); - + if (isAllowed(Permission.Type.EXTRACT_RUNS)){ getRunTable().setRowSelectionAllowed(true); getRunTable().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); @@ -1181,7 +1193,7 @@ public void run() { /** * This method initializes messagePanel - * + * * @return javax.swing.JPanel */ private JPanel getMessagePanel() { @@ -1205,7 +1217,7 @@ private JPanel getMessagePanel() { /** * This method initializes buttonPanel - * + * * @return javax.swing.JPanel */ private JPanel getButtonPanel() { @@ -1231,7 +1243,7 @@ private JPanel getButtonPanel() { /** * This method initializes requestRunButton - * + * * @return javax.swing.JButton */ private JButton getRequestRunButton() { @@ -1241,6 +1253,7 @@ private JButton getRequestRunButton() { requestRunButton.setToolTipText("Request the selected Run for Judging"); requestRunButton.setMnemonic(java.awt.event.KeyEvent.VK_R); requestRunButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { requestSelectedRun(); } @@ -1342,7 +1355,7 @@ protected void rejudgeSelectedRun() { /** * This method initializes filterButton - * + * * @return javax.swing.JButton */ private JButton getFilterButton() { @@ -1352,6 +1365,7 @@ private JButton getFilterButton() { filterButton.setToolTipText("Edit Filter"); filterButton.setMnemonic(java.awt.event.KeyEvent.VK_F); filterButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { showFilterRunsFrame(); } @@ -1364,7 +1378,7 @@ protected void showFilterRunsFrame() { getEditFilterFrame().addList(ListNames.PROBLEMS); getEditFilterFrame().addList(ListNames.JUDGEMENTS); getEditFilterFrame().addList(ListNames.LANGUAGES); - + if (! usingTeamColumns) { getEditFilterFrame().addList(ListNames.TEAM_ACCOUNTS); getEditFilterFrame().addList(ListNames.RUN_STATES); @@ -1378,7 +1392,7 @@ protected void showFilterRunsFrame() { /** * This method initializes editRunButton - * + * * @return javax.swing.JButton */ private JButton getEditRunButton() { @@ -1388,6 +1402,7 @@ private JButton getEditRunButton() { editRunButton.setToolTipText("Edit the selected Run"); editRunButton.setMnemonic(java.awt.event.KeyEvent.VK_E); editRunButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { editSelectedRun(); } @@ -1419,7 +1434,7 @@ protected void editSelectedRun() { /** * This method initializes extractButton - * + * * @return javax.swing.JButton */ private JButton getExtractButton() { @@ -1428,9 +1443,10 @@ private JButton getExtractButton() { extractButton.setText("Extract"); extractButton.setMnemonic(java.awt.event.KeyEvent.VK_X); extractButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { extractRuns(getRunTable()); - + } }); } @@ -1489,7 +1505,7 @@ private ElementId[] getRunKeys(JTableCustomized runs) { for (int rowNumber = 0; rowNumber < totalRows; rowNumber++) { vector.addElement(runs.getElementIdFromTableRow(rowNumber)); } - return (ElementId[]) vector.toArray(new ElementId[vector.size()]); + return vector.toArray(new ElementId[vector.size()]); } private ElementId[] getRunKeys(JTableCustomized runs, int[] selectedRows) { @@ -1497,25 +1513,25 @@ private ElementId[] getRunKeys(JTableCustomized runs, int[] selectedRows) { for (int rowNumber : selectedRows) { vector.addElement(runs.getElementIdFromTableRow(rowNumber)); } - return (ElementId[]) vector.toArray(new ElementId[vector.size()]); + return vector.toArray(new ElementId[vector.size()]); } private int extractSelectedRuns(JTableCustomized runs, ElementId[] runKeys) { int extractCount = 0; - + int totalRows = runKeys.length; - + for (int i = 0; i < runKeys.length; i++) { try { boolean extracted = extractRuns.extractRun(runKeys[i]); - + if (extracted){ extractCount ++; } - + updateRunCount (extractCount, totalRows); - + } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -1540,7 +1556,7 @@ private void showMessageToUser(String message) { /** * This method initializes giveButton - * + * * @return javax.swing.JButton */ private JButton getGiveButton() { @@ -1550,6 +1566,7 @@ private JButton getGiveButton() { giveButton.setToolTipText("Give the selected Run back to Judges"); giveButton.setMnemonic(java.awt.event.KeyEvent.VK_G); giveButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { giveSelectedRun(); } @@ -1587,7 +1604,7 @@ protected void giveSelectedRun() { /** * This method initializes takeButton - * + * * @return javax.swing.JButton */ private JButton getTakeButton() { @@ -1597,6 +1614,7 @@ private JButton getTakeButton() { takeButton.setToolTipText("Take the selected Run from the Judges"); takeButton.setMnemonic(java.awt.event.KeyEvent.VK_T); takeButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { System.out.println("TODO RunsTable.getTakeButton actionPerformed()"); // TODO code Take Run @@ -1608,7 +1626,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { /** * This method initializes rejudgeRunButton - * + * * @return javax.swing.JButton */ private JButton getRejudgeRunButton() { @@ -1617,6 +1635,7 @@ private JButton getRejudgeRunButton() { rejudgeRunButton.setText("Rejudge"); rejudgeRunButton.setToolTipText("Rejudge the selected Run"); rejudgeRunButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { rejudgeSelectedRun(); } @@ -1627,7 +1646,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { /** * This method initializes viewJudgementsButton - * + * * @return javax.swing.JButton */ private JButton getViewJudgementsButton() { @@ -1636,6 +1655,7 @@ private JButton getViewJudgementsButton() { viewJudgementsButton.setText("View Judgements"); viewJudgementsButton.setToolTipText("View Judgements for the selected Run"); viewJudgementsButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { viewSelectedRunJudgements(); } @@ -1669,27 +1689,29 @@ protected void viewSelectedRunJudgements() { } } - + public boolean isDisplayConfirmation() { return displayConfirmation; } - + public void setDisplayConfirmation(boolean displayConfirmation) { this.displayConfirmation = displayConfirmation; } /** * Account Listener. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { // ignore doesn't affect this pane } + @Override public void accountModified(AccountEvent event) { // check if is this account Account account = event.getAccount(); @@ -1700,6 +1722,7 @@ public void accountModified(AccountEvent event) { // They modified us!! initializePermissions(); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateGUIperPermissions(); reloadRunList(); @@ -1709,6 +1732,7 @@ public void run() { } else { // not us, but update the grid anyways SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } @@ -1718,11 +1742,13 @@ public void run() { } + @Override public void accountsAdded(AccountEvent accountEvent) { // ignore, this does not affect this class } + @Override public void accountsModified(AccountEvent accountEvent) { // check if it included this account boolean theyModifiedUs = false; @@ -1737,6 +1763,7 @@ public void accountsModified(AccountEvent accountEvent) { } final boolean finalTheyModifiedUs = theyModifiedUs; SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { if (finalTheyModifiedUs) { updateGUIperPermissions(); @@ -1746,6 +1773,7 @@ public void run() { }); } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { accountsModified(accountEvent); } @@ -1753,63 +1781,75 @@ public void accountsRefreshAll(AccountEvent accountEvent) { /** * Problem Listener. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ public class ProblemListenerImplementation implements IProblemListener { + @Override public void problemAdded(ProblemEvent event) { // ignore does not affect this pane } + @Override public void problemChanged(ProblemEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } }); } + @Override public void problemRemoved(ProblemEvent event) { // ignore does not affect this pane } + @Override public void problemRefreshAll(ProblemEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } - }); + }); } } /** * Language Listener. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ public class LanguageListenerImplementation implements ILanguageListener { + @Override public void languageAdded(LanguageEvent event) { // ignore does not affect this pane } + @Override public void languageChanged(LanguageEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } }); } + @Override public void languageRemoved(LanguageEvent event) { // ignore does not affect this pane } + @Override public void languageRefreshAll(LanguageEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } @@ -1819,6 +1859,7 @@ public void run() { @Override public void languagesAdded(LanguageEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } @@ -1828,6 +1869,7 @@ public void run() { @Override public void languagesChanged(LanguageEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } @@ -1837,44 +1879,53 @@ public void run() { /** * Contest Information Listener. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ public class ContestInformationListenerImplementation implements IContestInformationListener { + @Override public void contestInformationAdded(ContestInformationEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } }); } + @Override public void contestInformationChanged(ContestInformationEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } }); } + @Override public void contestInformationRemoved(ContestInformationEvent event) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } }); } + @Override public void contestInformationRefreshAll(ContestInformationEvent contestInformationEvent) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { reloadRunList(); } }); } + @Override public void finalizeDataChanged(ContestInformationEvent contestInformationEvent) { // Not used } @@ -1885,6 +1936,7 @@ public void finalizeDataChanged(ContestInformationEvent contestInformationEvent) private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); messageLabel.setToolTipText(string); @@ -1899,12 +1951,12 @@ public boolean isShowNewRunsOnly() { /** * Shows only runs that are RunStates.NEW and RunStates.MANUAL_REVIEW. - * + * * @param showNewRunsOnly */ public void setShowNewRunsOnly(boolean showNewRunsOnly) { this.showNewRunsOnly = showNewRunsOnly; - + if (showNewRunsOnly) { if (requiredFilter == null) { requiredFilter = new Filter(); @@ -1926,7 +1978,7 @@ public void setShowJudgesInfo(boolean showJudgesInfo) { /** * This method initializes autoJudgeButton - * + * * @return javax.swing.JButton */ private JButton getAutoJudgeButton() { @@ -1936,6 +1988,7 @@ private JButton getAutoJudgeButton() { autoJudgeButton.setText("Auto Judge"); autoJudgeButton.setToolTipText("Enable Auto Judging"); autoJudgeButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { // Turn auto judging on if (!bUseAutoJudgemonitor) { @@ -1953,7 +2006,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { /** * Is Auto Judging turned On for this judge ? - * + * * @return */ private boolean isAutoJudgingEnabled() { @@ -1969,10 +2022,10 @@ public void startAutoJudging() { if (!bUseAutoJudgemonitor) { return; } - + if (isAutoJudgingEnabled()) { showMessage(""); - + // make sure the OS supports judging of all problems this judge // is set up to autojudge BEFORE starting autojudging. List plist = autoJudgingMonitor.getOSUnsupportedAutojudgeProblemList(); @@ -1990,6 +2043,7 @@ public void startAutoJudging() { } // Keep this off the AWT thread. new Thread(new Runnable() { + @Override public void run() { // RE-enable local auto judge flag autoJudgingMonitor.startAutoJudging(); @@ -1997,7 +2051,7 @@ public void run() { }).start(); } else { showMessage("Administrator has turned off Auto Judging"); - + List plist = OSCompatibilityUtilities.getUnableToJudgeList(getContest(), log); if(!plist.isEmpty()) { StringBuffer message = new StringBuffer(); @@ -2024,6 +2078,7 @@ public void setMakeSoundOnOneRun(boolean makeSoundOnOneRun) { public EditFilterFrame getEditFilterFrame() { if (editFilterFrame == null){ Runnable callback = new Runnable(){ + @Override public void run() { reloadRunList(); } @@ -2035,10 +2090,10 @@ public void run() { } return editFilterFrame; } - + /** * Set title for the Filter Frame. - * + * * @param title */ public void setFilterFrameTitle (String title){ @@ -2047,25 +2102,29 @@ public void setFilterFrameTitle (String title){ editFilterFrame.setTitle(title); } } - + /** * @author pc2@ecs.csus.edu * */ public class BalloonSettingsListenerImplementation implements IBalloonSettingsListener { + @Override public void balloonSettingsAdded(BalloonSettingsEvent event) { reloadRunList(); } + @Override public void balloonSettingsChanged(BalloonSettingsEvent event) { reloadRunList(); } + @Override public void balloonSettingsRemoved(BalloonSettingsEvent event) { reloadRunList(); } + @Override public void balloonSettingsRefreshAll(BalloonSettingsEvent balloonSettingsEvent) { reloadRunList(); } @@ -2080,7 +2139,7 @@ private void BlockViewSource() { showSourceActive = true; } - + /** * @author pc2@ecs.csus.edu * @@ -2090,16 +2149,17 @@ private void AllowViewSource() { showSourceActive = false; } - + private boolean IsAllowedViewSource() { return (showSourceActive == false); } - + private JButton getViewSourceButton() { if (viewSourceButton == null) { viewSourceButton = new JButton("View Source"); viewSourceButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { // For now, only allow one View Source to be outstanding at-a-time @@ -2111,12 +2171,12 @@ public void actionPerformed(ActionEvent e) { getController().getLog().log(Log.INFO, "There is already a View Source pending for Run " + requestedRun.getNumber() + " at site " + requestedRun.getSiteNumber()); } else { // This could mean that the requestedRun's info just came in. - showMessage("There is already a View Source pending"); + showMessage("There is already a View Source pending"); getController().getLog().log(Log.INFO, "There is already a View Source pending but no requestedRun"); } return; } - + // make sure we're allowed to fetch a run if (!isAllowed(Permission.Type.ALLOWED_TO_FETCH_RUN)) { getController().getLog().log(Log.WARNING, "Account does not have the permission ALLOWED_TO_FETCH_RUN; cannot view run source."); @@ -2142,6 +2202,7 @@ public void actionPerformed(ActionEvent e) { // }); Thread viewSourceThread = new Thread() { + @Override public void run() { showSourceForSelectedRun(selectedIndexes[0]); } @@ -2156,18 +2217,18 @@ public void run() { } return viewSourceButton; } - + /** * Displays a {@link MultipleFileViewer} containing the source code for the run (submission) which is currently selected in the Runs grid. - * + * * If no run is selected, or more than one run is selected, prompts the user to select just one run (row) in the grid * and does nothing else. */ private void showSourceForSelectedRun(int nSelectedRunIndex) { boolean bFetchError = false; - - // we are allowed to view source and there's exactly one run selected; try to obtain the run source and display it in a MFV + + // we are allowed to view source and there's exactly one run selected; try to obtain the run source and display it in a MFV try { Run run = getContest().getRun(runTable.getElementIdFromTableRow(nSelectedRunIndex)); @@ -2178,18 +2239,18 @@ private void showSourceForSelectedRun(int nSelectedRunIndex) { showMessage("Preparing to display source code for run " + run.getNumber() + " at site " + run.getSiteNumber()); getController().getLog().log(Log.INFO, "Preparing to display source code for run " + run.getNumber() + " at site " + run.getSiteNumber()); - //the following forces a (read-only) checkout from the server; it makes more sense to first see if we already have the + //the following forces a (read-only) checkout from the server; it makes more sense to first see if we already have the // necessary RunFiles and then if not to issue a "Fetch" request rather than a "checkout" request // getController().checkOutRun(run, true, false); // checkoutRun(run, isReadOnlyRequest, isComputerJudgedRequest) //check if we already have the RunFiles for the run if (!getContest().isRunFilesPresent(run)) { - + // this is the run we're after requestedRun = run; // reset this each time so we be sure to get the first new reply fetchedRun = null; - + //we don't have the files; request them from the server (this is NOT a checkout, but a read-only fetch) getController().fetchRun(run); @@ -2202,56 +2263,56 @@ private void showSourceForSelectedRun(int nSelectedRunIndex) { } // no longer interested in getting updates for this run. requestedRun = null; - + //check if we got a reply from the server if (serverReplied) { - + //the server replied; see if we got some RunFiles if (fetchedRunFiles!=null) { - + //we got some RunFiles from the server; put them into the contest model getContest().updateRunFiles(run, fetchedRunFiles); } else { - + //we got a reply from the server but we didn't get any RunFiles getController().getLog().log(Log.WARNING, "Server failed to return RunFiles in response to fetch run request"); bFetchError = true; } - + } else { - + // the server failed to reply to the fetchRun request within the time limit getController().getLog().log(Log.WARNING, "No response from server to fetch run request after " + waitedMS + "ms"); bFetchError = true; } } - + // OK to now start another view source AllowViewSource(); - + // if any type of error occurred requesting the run to view, log a summary message and finish if(bFetchError) { getController().getLog().log(Log.WARNING, "Unable to fetch run " + run.getNumber() + " at site " + run.getSiteNumber() + " from server"); showMessage("Unable to fetch selected run; check log"); } else { - + //if we get here we know there should be RunFiles in the contest model -- but let's sanity-check that if (!getContest().isRunFilesPresent(run)) { - + //something bad happened -- we SHOULD have RunFiles at this point! getController().getLog().log(Log.SEVERE, "Unable to find RunFiles for run " + run.getNumber() + " at site " + run.getSiteNumber() + " -- server error?"); - showMessage("Unable to fetch selected run; check log"); + showMessage("Unable to fetch selected run; check log"); } else { - + //get the RunFiles RunFiles runFiles = getContest().getRunFiles(run); - + if (runFiles != null) { - + // get the (serialized) source files out of the RunFiles SerializedFile mainFile = runFiles.getMainFile(); SerializedFile[] otherFiles = runFiles.getOtherFiles(); - + // create a MultiFileViewer in which to display the runFiles // Note: previously used 'fetchedRun' here for site/number; it is possible that those values are not // correct if the run files are already present; in that case, we would have never asked for them to be @@ -2260,7 +2321,7 @@ private void showSourceForSelectedRun(int nSelectedRunIndex) { // server request was ever made for a run's files. MultipleFileViewer mfv = new MultipleFileViewer(log, "Source files for Site " + run.getSiteNumber() + " Run " + run.getNumber()); mfv.setContestAndController(getContest(), getController()); - + // if entry point was specified, add a tab for it if(run.getEntryPoint() != null) { mfv.addTextPane("Entry Point", run.getEntryPoint()); @@ -2275,7 +2336,7 @@ private void showSourceForSelectedRun(int nSelectedRunIndex) { otherFilesLoadedOK &= mfv.addFilePane(otherFile.getName(), otherFile); } } - + // add the mainFile to the MFV boolean mainFilePresent = false; boolean mainFileLoadedOK = false; @@ -2283,9 +2344,9 @@ private void showSourceForSelectedRun(int nSelectedRunIndex) { mainFilePresent = true; mainFileLoadedOK = mfv.addFilePane("Main File" + " (" + mainFile.getName() + ")", mainFile); } - + // if we successfully added all files, show the MFV - if ((!mainFilePresent || (mainFilePresent && mainFileLoadedOK)) + if ((!mainFilePresent || (mainFilePresent && mainFileLoadedOK)) && (!otherFilesPresent || (otherFilesPresent && otherFilesLoadedOK))) { mfv.setSelectedIndex(0); //always make leftmost selected; normally this will be MainFile mfv.setVisible(true); @@ -2294,13 +2355,13 @@ private void showSourceForSelectedRun(int nSelectedRunIndex) { getController().getLog().log(Log.WARNING, "Unable to load run source files into MultiFileViewer"); showMessage("Unable to load run source files into MultiFileViewer"); } - + } else { // runfiles is null getController().getLog().log(Log.WARNING, "Unable to obtain RunFiles for Site " + run.getSiteNumber() + " run " + run.getNumber()); showMessage("Unable to obtain RunFiles for selected run"); } - + } } return; @@ -2313,11 +2374,11 @@ private void showSourceForSelectedRun(int nSelectedRunIndex) { } catch (Exception e) { getController().getLog().log(Log.WARNING, "Exception logged ", e); showMessage("Unable to show run source, check log"); - + //make sure this is clear in case of exception requestedRun = null; } - + // OK to now start another view source now AllowViewSource(); } diff --git a/src/edu/csus/ecs/pc2/ui/StandingsHTMLPane.java b/src/edu/csus/ecs/pc2/ui/StandingsHTMLPane.java index f2f80a520..9428dd758 100644 --- a/src/edu/csus/ecs/pc2/ui/StandingsHTMLPane.java +++ b/src/edu/csus/ecs/pc2/ui/StandingsHTMLPane.java @@ -1,33 +1,45 @@ -// 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.BorderLayout; +import java.awt.Dimension; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.Properties; +import javax.swing.DefaultListModel; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextPane; +import javax.swing.ListModel; import javax.swing.SwingUtilities; import javax.swing.text.html.HTMLEditorKit; import edu.csus.ecs.pc2.VersionInfo; import edu.csus.ecs.pc2.core.IInternalController; import edu.csus.ecs.pc2.core.exception.IllegalContestState; +import edu.csus.ecs.pc2.core.list.GroupComparator; import edu.csus.ecs.pc2.core.log.Log; import edu.csus.ecs.pc2.core.model.AccountEvent; import edu.csus.ecs.pc2.core.model.BalloonSettingsEvent; import edu.csus.ecs.pc2.core.model.ContestInformationEvent; +import edu.csus.ecs.pc2.core.model.Group; +import edu.csus.ecs.pc2.core.model.GroupEvent; import edu.csus.ecs.pc2.core.model.IAccountListener; import edu.csus.ecs.pc2.core.model.IBalloonSettingsListener; -import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.IContestInformationListener; +import edu.csus.ecs.pc2.core.model.IGroupListener; +import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.IProblemListener; import edu.csus.ecs.pc2.core.model.IRunListener; import edu.csus.ecs.pc2.core.model.ProblemEvent; @@ -39,10 +51,10 @@ /** * Standings HTML view pane. - * + * * Use the input style sheet to show a view of the contest standings. *

    - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -51,7 +63,7 @@ public class StandingsHTMLPane extends JPanePlugin { /** - * + * */ private static final long serialVersionUID = -1570509469246188861L; @@ -79,6 +91,12 @@ public class StandingsHTMLPane extends JPanePlugin { private JScrollPane scrollPane = null; + private JScrollPane groupPane = null; + + private JCheckBoxJList groupsJList = null; + + private ListModel groupsListModel = new DefaultListModel(); + private StandingsHTMLPane() { super(); initialize(); @@ -97,13 +115,14 @@ public StandingsHTMLPane(String styleSheetFileName, String styleSheetDirectoryNa /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); - this.setSize(new java.awt.Dimension(470, 243)); + this.setSize(new java.awt.Dimension(470, 543)); this.add(getMessagePane(), java.awt.BorderLayout.NORTH); this.add(getScrollPane(), java.awt.BorderLayout.CENTER); + this.add(getGroupsPane(), java.awt.BorderLayout.EAST); } @Override @@ -111,6 +130,7 @@ public String getPluginTitle() { return "Standings HTML Plugin"; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); @@ -121,6 +141,8 @@ public void setContestAndController(IInternalContest inContest, IInternalControl getContest().addRunListener(new RunListenerImplementation()); getContest().addContestInformationListener(new ContestInformationListenerImplementation()); getContest().addBalloonSettingsListener(new BalloonSettingsListenerImplementation()); + getContest().addGroupListener(new GroupListenerImplementation()); + populateGroupsList(); refreshStandings(); } @@ -138,9 +160,9 @@ private String getDefaultSyleSheetDirectoryName() { /** * Refresh Standings. - * + * * Regenerate standings and display new HTML on pane. - * + * */ public void refreshStandings() { @@ -152,10 +174,24 @@ public void refreshStandings() { showMessage(""); + Object [] gobjs = getGroupsJList().getSelectedValues(); + ArrayList garray = null; + + if(gobjs.length > 0) { + garray = new ArrayList(); + for(Object o : gobjs ) { + JCheckBox groupCheck = (JCheckBox)o; + Group group = (Group)groupCheck.getClientProperty("group"); + if(group != null) { + garray.add(group); + } + } + } + String xmlString; try { - Properties scoringProperties = getScoringProperties(); - xmlString = scoringAlgorithm.getStandings(getContest(), scoringProperties, log); + Properties scoringProperties = getScoringProperties(); + xmlString = scoringAlgorithm.getStandings(getContest(), null, null, garray, scoringProperties, log); transformAndDisplay(xmlString, styleSheetFileName); showMessage("Last update " + new Date()); } catch (IllegalContestState e) { @@ -170,19 +206,19 @@ protected Properties getScoringProperties() { if (properties == null){ properties = new Properties(); } - + Properties defProperties = DefaultScoringAlgorithm.getDefaultProperties(); /** * Fill in with default properties if not using them. */ - String [] keys = (String[]) defProperties.keySet().toArray(new String[defProperties.keySet().size()]); + String [] keys = defProperties.keySet().toArray(new String[defProperties.keySet().size()]); for (String key : keys) { if (! properties.containsKey(key)){ properties.put(key, defProperties.get(key)); } } - + return properties; } @@ -204,7 +240,7 @@ private void transformAndDisplay(String xmlString, String xsltFileName) { File xslFile = new File(fullPathFileName); final String htmlString = xslTransformer.transformToString(xslFile, xmlString); viewHTML (htmlString); - + } catch (Exception e) { log.log(Log.WARNING, "Error generating web/html display ", e); showMessage("Error generating web/html display, check logs"); @@ -216,25 +252,26 @@ private void viewHTML(String htmlString) { try { File tmpFile = File.createTempFile("__t",".htm"); tmpFile.deleteOnExit(); - + BufferedWriter out = new BufferedWriter(new FileWriter(tmpFile)); out.write(htmlString); out.close(); out = null; - + final URL tmpURL = tmpFile.toURI().toURL(); - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { try { getTextArea().setPage(tmpURL); } catch (Exception e) { log.log(Log.WARNING,"Could not display HTML", e); } - + } }); - + } catch (Exception e) { log.log(Log.WARNING, "In View HTML ", e); } @@ -242,7 +279,7 @@ public void run() { /** * This method initializes messagePane - * + * * @return javax.swing.JPanel */ private JPanel getMessagePane() { @@ -262,6 +299,7 @@ private JPanel getMessagePane() { private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); } @@ -273,22 +311,27 @@ public void run() { */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { refreshStandings(); } + @Override public void accountModified(AccountEvent event) { refreshStandings(); } + @Override public void accountsAdded(AccountEvent accountEvent) { refreshStandings(); } + @Override public void accountsModified(AccountEvent accountEvent) { refreshStandings(); } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { refreshStandings(); } @@ -300,18 +343,22 @@ public void accountsRefreshAll(AccountEvent accountEvent) { */ public class ProblemListenerImplementation implements IProblemListener { + @Override public void problemAdded(ProblemEvent event) { refreshStandings(); } + @Override public void problemChanged(ProblemEvent event) { refreshStandings(); } + @Override public void problemRemoved(ProblemEvent event) { refreshStandings(); } + @Override public void problemRefreshAll(ProblemEvent event) { refreshStandings(); } @@ -319,25 +366,29 @@ public void problemRefreshAll(ProblemEvent event) { } /** - * + * * @author pc2@ecs.csus.edu */ public class RunListenerImplementation implements IRunListener { + @Override public void runAdded(RunEvent event) { // ignore } - + + @Override public void refreshRuns(RunEvent event) { - refreshStandings(); + refreshStandings(); } + @Override public void runChanged(RunEvent event) { if (event.getAction().equals(Action.CHANGED)) { refreshStandings(); } } + @Override public void runRemoved(RunEvent event) { refreshStandings(); } @@ -346,30 +397,35 @@ public void runRemoved(RunEvent event) { /** * Contest Information Listener for StandingsHTMLPane. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ - + // $HeadURL$ class ContestInformationListenerImplementation implements IContestInformationListener { + @Override public void contestInformationAdded(ContestInformationEvent event) { refreshStandings(); } + @Override public void contestInformationChanged(ContestInformationEvent event) { refreshStandings(); } + @Override public void contestInformationRemoved(ContestInformationEvent event) { // ignored } + @Override public void contestInformationRefreshAll(ContestInformationEvent contestInformationEvent) { refreshStandings(); } - + + @Override public void finalizeDataChanged(ContestInformationEvent contestInformationEvent) { refreshStandings(); } @@ -383,27 +439,70 @@ public void finalizeDataChanged(ContestInformationEvent contestInformationEvent) */ public class BalloonSettingsListenerImplementation implements IBalloonSettingsListener { + @Override public void balloonSettingsAdded(BalloonSettingsEvent event) { refreshStandings(); } + @Override public void balloonSettingsChanged(BalloonSettingsEvent event) { refreshStandings(); } + @Override public void balloonSettingsRemoved(BalloonSettingsEvent event) { refreshStandings(); } + @Override public void balloonSettingsRefreshAll(BalloonSettingsEvent balloonSettingsEvent) { refreshStandings(); } - + + } + + /** + * If any groups change, we need to possibly update our checkbox lists + * + * @author John Buck + * + */ + public class GroupListenerImplementation implements IGroupListener { + + @Override + public void groupAdded(GroupEvent event) { + commonGroupUpdate(); + } + + @Override + public void groupChanged(GroupEvent event) { + commonGroupUpdate(); + } + + @Override + public void groupRemoved(GroupEvent event) { + commonGroupUpdate(); + } + + @Override + public void groupRefreshAll(GroupEvent groupEvent) { + commonGroupUpdate(); + } + + @Override + public void groupsAdded(GroupEvent event) { + commonGroupUpdate(); + } + + @Override + public void groupsChanged(GroupEvent event) { + commonGroupUpdate(); + } } /** * This method initializes textArea - * + * * @return javax.swing.JTextPane */ private JTextPane getTextArea() { @@ -421,7 +520,7 @@ public String getStyleSheetFileName() { /** * Set the name of the Stylesheet filename. - * + * * @param styleSheetFileName */ public void setStyleSheetFileName(String styleSheetFileName) { @@ -430,7 +529,7 @@ public void setStyleSheetFileName(String styleSheetFileName) { /** * This method initializes refreshButton - * + * * @return javax.swing.JButton */ private JButton getRefreshButton() { @@ -439,6 +538,7 @@ private JButton getRefreshButton() { refreshButton.setText("Refresh"); refreshButton.setToolTipText("Refresh display"); refreshButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { refreshStandings(); } @@ -460,7 +560,7 @@ public void setStyleSheetDirectoryName(String styleSheetDirectoryName) { /** * This method initializes scrollPane - * + * * @return javax.swing.JScrollPane */ private JScrollPane getScrollPane() { @@ -470,5 +570,68 @@ private JScrollPane getScrollPane() { } return scrollPane; } + /** + * This method initializes groups ScrollPane + * + * @return javax.swing.JScrollPane + */ + private JScrollPane getGroupsPane() { + if (groupPane == null) { + groupPane = new JScrollPane(); + groupPane.setPreferredSize(new Dimension(180, 200)); +// groupPane.setBounds(new java.awt.Rectangle(14, 14, 200, 200)); + groupPane.setViewportView(getGroupsJList()); + } + return groupPane; + } + + private void populateGroupsList() { + ((DefaultListModel) groupsListModel).removeAllElements(); + + Group [] allgroups = getContest().getGroups(); + Arrays.sort(allgroups, new GroupComparator()); + for(Group group : allgroups ) { + if(group.isDisplayOnScoreboard()) { + addGroupCheckBox(group); + } + } + + } + + private void addGroupCheckBox(Group group) { + JCheckBox checkBox = new JCheckBox(group.getDisplayName()); + checkBox.putClientProperty("group", group); + ((DefaultListModel) groupsListModel).addElement(checkBox); + } + + /** + * This method initializes groupsJList + * + * @return javax.swing.JList + */ + private JCheckBoxJList getGroupsJList() { + if (groupsJList == null) { + groupsJList = new JCheckBoxJList(); + groupsJList.setModel(groupsListModel); + + // ListSelectionListeners are called before JCheckBoxes get updated + groupsJList.addPropertyChangeListener("change", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + refreshStandings(); + } + }); + } + return groupsJList; + } + + /** + * Called when groups change to force regeneration of group checklists and possibly + * the standings if a removed group was checked. + */ + private void commonGroupUpdate() { + populateGroupsList(); + refreshStandings(); + } } // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/StandingsTablePane.java b/src/edu/csus/ecs/pc2/ui/StandingsTablePane.java index 82d0fe111..30fb5e7d3 100644 --- a/src/edu/csus/ecs/pc2/ui/StandingsTablePane.java +++ b/src/edu/csus/ecs/pc2/ui/StandingsTablePane.java @@ -1,17 +1,24 @@ -// 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.BorderLayout; import java.awt.Component; import java.awt.Dimension; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.Properties; +import javax.swing.DefaultListModel; +import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; +import javax.swing.ListModel; import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; @@ -30,14 +37,18 @@ import org.xml.sax.InputSource; import edu.csus.ecs.pc2.core.IInternalController; +import edu.csus.ecs.pc2.core.list.GroupComparator; import edu.csus.ecs.pc2.core.log.Log; import edu.csus.ecs.pc2.core.model.AccountEvent; import edu.csus.ecs.pc2.core.model.BalloonSettingsEvent; import edu.csus.ecs.pc2.core.model.ContestInformationEvent; +import edu.csus.ecs.pc2.core.model.Group; +import edu.csus.ecs.pc2.core.model.GroupEvent; import edu.csus.ecs.pc2.core.model.IAccountListener; import edu.csus.ecs.pc2.core.model.IBalloonSettingsListener; -import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.IContestInformationListener; +import edu.csus.ecs.pc2.core.model.IGroupListener; +import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.IProblemListener; import edu.csus.ecs.pc2.core.model.IRunListener; import edu.csus.ecs.pc2.core.model.ProblemEvent; @@ -47,19 +58,19 @@ /** * Standings Table Pane. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ // $HeadURL$ public class StandingsTablePane extends JPanePlugin { - + /** - * + * */ private static final long serialVersionUID = -7721246142538681421L; - + private static final int VERT_PAD = 2; private static final int HORZ_PAD = 20; private static final int HORZ_INSET = 5; @@ -70,16 +81,22 @@ public class StandingsTablePane extends JPanePlugin { private JPanel messagePane = null; private JLabel messageLabel = null; - + private JScrollPane scrollPane = null; private Log log; private String currentXMLString = ""; - + + private JScrollPane groupPane = null; + + private JCheckBoxJList groupsJList = null; + + private ListModel groupsListModel = new DefaultListModel(); + /** * This method initializes - * + * */ public StandingsTablePane() { super(); @@ -88,13 +105,14 @@ public StandingsTablePane() { /** * This method initializes this - * + * */ private void initialize() { this.setLayout(new BorderLayout()); - this.setSize(new java.awt.Dimension(470, 243)); + this.setSize(new java.awt.Dimension(470, 543)); this.add(getMessagePane(), java.awt.BorderLayout.NORTH); this.add(getStandingsPane(), java.awt.BorderLayout.CENTER); + this.add(getGroupsPane(), java.awt.BorderLayout.EAST); } @@ -103,29 +121,32 @@ public String getPluginTitle() { return "Standings Table Plugin"; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { super.setContestAndController(inContest, inController); - + log = getController().getLog(); - + getContest().addAccountListener(new AccountListenerImplementation()); getContest().addProblemListener(new ProblemListenerImplementation()); getContest().addRunListener(new RunListenerImplementation()); getContest().addContestInformationListener(new ContestInformationListenerImplementation()); getContest().addBalloonSettingsListener(new BalloonSettingsListenerImplementation()); - + getContest().addGroupListener(new GroupListenerImplementation()); + + populateGroupsList(); refreshStandings(); } - + /** * Fetch string from nodes. - * + * * @param node * @return */ private Object [] fetchStanding(Node node) { - + // Object[] cols = { "Rank", "Name", "Solved", "Points" }; Object[] outArray = new Object[4]; @@ -148,10 +169,10 @@ public void setContestAndController(IInternalContest inContest, IInternalControl return outArray; } - + /** * This method initializes scrollPane - * + * * @return javax.swing.JScrollPane */ private JScrollPane getStandingsPane() { @@ -160,7 +181,7 @@ private JScrollPane getStandingsPane() { } return scrollPane; } - + /** * Parse output of ScoringAlgorithm and display. * @@ -170,34 +191,47 @@ protected void parseAndDisplay () { // Return value ignored; this just makes sure the table/table model are set up JTableCustomized atDummy = getStandingsTable(); standingsTableModel.setRowCount(0); - + Document document = null; String xmlString = null; + Object [] gobjs = getGroupsJList().getSelectedValues(); + ArrayList garray = null; + + if(gobjs.length > 0) { + garray = new ArrayList(); + for(Object o : gobjs ) { + JCheckBox groupCheck = (JCheckBox)o; + Group group = (Group)groupCheck.getClientProperty("group"); + if(group != null) { + garray.add(group); + } + } + } try { DefaultScoringAlgorithm defaultScoringAlgorithm = new DefaultScoringAlgorithm(); Properties properties = getScoringProperties(); - xmlString = defaultScoringAlgorithm.getStandings(getContest(), properties, getController().getLog()); + xmlString = defaultScoringAlgorithm.getStandings(getContest(), null, null, garray, properties, getController().getLog()); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); document = documentBuilder.parse(new InputSource(new StringReader(xmlString))); } catch (Exception e) { log.log(Log.WARNING, "Trouble creating or parsing SA XML ", e); showMessage("Problem updating scoreboard (parse error), check log"); - + return; // ----------------------------- RETURN ------------------------------- - + } - - - + + + try { // skip past nodes to find teamStanding node NodeList list = document.getDocumentElement().getChildNodes(); - + for(int i=0; i) groupsListModel).removeAllElements(); + + Group [] allgroups = getContest().getGroups(); + Arrays.sort(allgroups, new GroupComparator()); + for(Group group : allgroups ) { + if(group.isDisplayOnScoreboard()) { + addGroupCheckBox(group); + } + } + + } + + private void addGroupCheckBox(Group group) { + JCheckBox checkBox = new JCheckBox(group.getDisplayName()); + checkBox.putClientProperty("group", group); + ((DefaultListModel) groupsListModel).addElement(checkBox); + } + + /** + * This method initializes groupsJList + * + * @return javax.swing.JList + */ + private JCheckBoxJList getGroupsJList() { + if (groupsJList == null) { + groupsJList = new JCheckBoxJList(); + groupsJList.setModel(groupsListModel); + + // ListSelectionListeners are called before JCheckBoxes get updated + groupsJList.addPropertyChangeListener("change", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + refreshStandings(); + } + }); + } + return groupsJList; + } + + /** + * Called when groups change to force regeneration of group checklists and possibly + * the standings if a removed group was checked. + */ + private void commonGroupUpdate() { + populateGroupsList(); + refreshStandings(); + } + } // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/board/ScoreboardCommon.java b/src/edu/csus/ecs/pc2/ui/board/ScoreboardCommon.java index 588467667..8d1b025d9 100644 --- a/src/edu/csus/ecs/pc2/ui/board/ScoreboardCommon.java +++ b/src/edu/csus/ecs/pc2/ui/board/ScoreboardCommon.java @@ -1,6 +1,6 @@ -// 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.board; @@ -26,6 +26,7 @@ import edu.csus.ecs.pc2.core.exception.IllegalContestState; import edu.csus.ecs.pc2.core.log.Log; import edu.csus.ecs.pc2.core.model.FinalizeData; +import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.scoring.DefaultScoringAlgorithm; import edu.csus.ecs.pc2.core.util.XSLTransformer; @@ -120,6 +121,10 @@ public void copyIfNeeded(String inputDir, String file, String outputDir, Log log } public void generateOutput(String xmlString, String xslDir, String outputDir, Log log) { + generateOutput(xmlString, null, xslDir, outputDir, log); + } + + public void generateOutput(String xmlString, String groupName, String xslDir, String outputDir, Log log) { // FUTUREWORK move to to a common location (currently in both Module and View) File inputDir = new File(xslDir); if (!inputDir.isDirectory()) { @@ -143,22 +148,41 @@ public void generateOutput(String xmlString, String xslDir, String outputDir, Lo for (int i = 0; i < inputFiles.length; i++) { String xslFilename = inputFiles[i]; if (xslFilename.endsWith(".xsl")) { - String outputFilename = xslFilename.substring(0, xslFilename.length() - 4) + ".html"; + // file name minus ".xls" + String baseFilename = xslFilename.substring(0, xslFilename.length() - 4); try { File output = File.createTempFile("__t", ".htm", outputDirFile); FileOutputStream outputStream = new FileOutputStream(output); + String outputFilename; + transformer.transform(xslDir + File.separator + xslFilename, new ByteArrayInputStream(xmlString.getBytes()), outputStream); outputStream.close(); if (output.length() > 0) { - File outputFile = new File(outputDir + File.separator + outputFilename); + File outputFile = null; if (xslFilename.equals("pc2export.xsl")) { // change that, we want the pc2export written as a // .dat in the cwd outputFile = new File("pc2export.dat"); - } - // dump json and tsv and csv files in the html directory - if (xslFilename.endsWith(".json.xsl") || xslFilename.endsWith(".tsv.xsl") || xslFilename.endsWith(".csv.xsl") || xslFilename.endsWith(".php.xsl")) { - outputFile = new File(outputDir + File.separator + xslFilename.substring(0, xslFilename.length() - 4)); + } else { + String ext = null; + int trimLength = 0; + // dump json and tsv and csv files in the html directory + if(baseFilename.endsWith(".json")){ + trimLength = 5; + ext = ".json"; + } else if(baseFilename.endsWith(".tsv") || baseFilename.endsWith(".csv") || baseFilename.endsWith(".php")) { + trimLength = 4; + ext = baseFilename.substring(baseFilename.length() - 4); + } else { + ext = ".html"; + } + outputFilename = outputDir + File.separator + baseFilename.substring(0, baseFilename.length() - trimLength); + if(groupName != null) { + outputFilename = outputFilename + "_" + groupName; + } + outputFilename = outputFilename + ext; + + outputFile = new File(outputFilename); } // behaviour of renameTo is platform specific, try the // possibly atomic 1st @@ -199,14 +223,27 @@ public void generateOutput(String xmlString, String xslDir, String outputDir, Lo } public void generateResults(IInternalContest contest, IInternalController controller, String xmlString, String xslDir, Log log) { + generateResults(contest, controller, xmlString, null, xslDir, log); + } + + public void generateResults(IInternalContest contest, IInternalController controller, String xmlString, Group group, String xslDir, Log log) { + + String groupName = null; + if(group != null) { + groupName = group.getDisplayName(); + } + String resultsFilename = makeGroupFilename("results.xml", groupName); + try { File output = File.createTempFile("__t", ".tmp", new File(".")); FileOutputStream outputXML = new FileOutputStream(output); outputXML.write(xmlString.getBytes()); outputXML.close(); + if (output.length() > 0) { - File outputFile = new File("results.xml"); - // behaviour of renameTo is platform specific, try the possibly + + File outputFile = new File(resultsFilename); + // Behavior of renameTo is platform specific, try the possibly // atomic 1st if (!output.renameTo(outputFile)) { // otherwise fallback to the delete then rename @@ -217,14 +254,14 @@ public void generateResults(IInternalContest contest, IInternalController contro } } else { // 0 length file - log.warning("New results.xml is empty, not updating"); + log.warning("New " + resultsFilename + " is empty, not updating"); output.delete(); } output = null; } catch (FileNotFoundException e1) { - log.log(Log.WARNING, "Could not write to " + "results.xml", e1); + log.log(Log.WARNING, "Could not write to " + resultsFilename, e1); } catch (IOException e) { - log.log(Log.WARNING, "Problem writing to " + "results.xml", e); + log.log(Log.WARNING, "Problem writing to " + resultsFilename, e); } FinalizeData finalizeData = contest.getFinalizeData(); String outputDir = "."; @@ -243,8 +280,8 @@ public void generateResults(IInternalContest contest, IInternalController contro } try { ResultsFile resultsFile = new ResultsFile(); - String[] createTSVFileLines = resultsFile.createTSVFileLines(contest); - FileWriter outputFile = new FileWriter(outputResultsDirFile + File.separator + "results.tsv"); + String[] createTSVFileLines = resultsFile.createTSVFileLines(contest, group); + FileWriter outputFile = new FileWriter(outputResultsDirFile + File.separator + makeGroupFilename("results.tsv", groupName)); for (int i = 0; i < createTSVFileLines.length; i++) { outputFile.write(createTSVFileLines[i] + System.getProperty("line.separator")); } @@ -254,8 +291,8 @@ public void generateResults(IInternalContest contest, IInternalController contro } try { ScoreboardFile scoreboardFile = new ScoreboardFile(); - String[] createTSVFileLines = scoreboardFile.createTSVFileLines(contest); - FileWriter outputFile = new FileWriter(outputResultsDirFile + File.separator + "scoreboard.tsv"); + String[] createTSVFileLines = scoreboardFile.createTSVFileLines(contest, group); + FileWriter outputFile = new FileWriter(outputResultsDirFile + File.separator + makeGroupFilename("scoreboard.tsv", groupName)); for (int i = 0; i < createTSVFileLines.length; i++) { outputFile.write(createTSVFileLines[i] + System.getProperty("line.separator")); } @@ -265,8 +302,8 @@ public void generateResults(IInternalContest contest, IInternalController contro } StandingsJSON2016 standingsJson = new StandingsJSON2016(); try { - String createJSON = standingsJson.createJSON(contest, controller); - FileWriter outputFile = new FileWriter(outputResultsDirFile + File.separator + "scoreboard.json"); + String createJSON = standingsJson.createJSON(contest, controller, group); + FileWriter outputFile = new FileWriter(outputResultsDirFile + File.separator + makeGroupFilename("scoreboard.json", groupName)); outputFile.write(createJSON); outputFile.close(); } catch (IllegalContestState | IOException e) { @@ -275,6 +312,32 @@ public void generateResults(IInternalContest contest, IInternalController contro } } + /** + * Create a filename that has the groupname embedded in it, just before the extension. + * eg. scoreboard.json -> scoreboard_D1.json for example. + * If no extension is on the original filename, the groupname is appended. + * eg. scoreboard ->scoreboard_D1 + * + * @param filename to add group to + * @param groupName if null, then returns filename, otherwise adds the groupname to filename as described above + * @return filename that possibly contains the group name + */ + private String makeGroupFilename(String filename, String groupName) { + if(groupName == null || groupName.isEmpty()) { + return(filename); + } + + String newFilename; + int extIndex = filename.lastIndexOf('.'); + if(extIndex >= 0) { + newFilename = filename.substring(0, extIndex) + "_" + groupName + filename.substring(extIndex); + + } else { + newFilename = filename + "_" + groupName; + } + return(newFilename); + } + protected Properties getScoringProperties(Properties properties) { if (properties == null) { properties = new Properties(); @@ -285,7 +348,7 @@ protected Properties getScoringProperties(Properties properties) { /** * Fill in with default properties if not using them. */ - String[] keys = (String[]) defProperties.keySet().toArray(new String[defProperties.keySet().size()]); + String[] keys = defProperties.keySet().toArray(new String[defProperties.keySet().size()]); for (String key : keys) { if (!properties.containsKey(key)) { properties.put(key, defProperties.get(key)); diff --git a/src/edu/csus/ecs/pc2/ui/board/ScoreboardModule.java b/src/edu/csus/ecs/pc2/ui/board/ScoreboardModule.java index 8cbd9611f..6e44f54e4 100644 --- a/src/edu/csus/ecs/pc2/ui/board/ScoreboardModule.java +++ b/src/edu/csus/ecs/pc2/ui/board/ScoreboardModule.java @@ -1,8 +1,9 @@ -// 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.board; import java.io.File; import java.text.DateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.Locale; import java.util.Properties; @@ -17,6 +18,7 @@ import edu.csus.ecs.pc2.core.model.ContestInformationEvent; import edu.csus.ecs.pc2.core.model.ContestTime; import edu.csus.ecs.pc2.core.model.ContestTimeEvent; +import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IAccountListener; import edu.csus.ecs.pc2.core.model.IBalloonSettingsListener; import edu.csus.ecs.pc2.core.model.IContestInformationListener; @@ -33,13 +35,13 @@ /** * A non-GUI Scoreboard Module. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class ScoreboardModule implements UIPlugin { /** - * + * */ private static final long serialVersionUID = 5352802558674673586L; @@ -52,7 +54,7 @@ public class ScoreboardModule implements UIPlugin { private String xslDir; private DefaultScoringAlgorithm algo = new DefaultScoringAlgorithm(); - + /* * We set setObeyFrozen = true on this one. */ @@ -60,6 +62,7 @@ public class ScoreboardModule implements UIPlugin { private ScoreboardCommon scoreboardCommon = new ScoreboardCommon(); + @Override public String getPluginTitle() { return "Scoreboard (non-GUI)"; } @@ -74,6 +77,7 @@ public ScoreboardModule() { System.out.println(); } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { contest = inContest; controller = inController; @@ -124,27 +128,49 @@ private void startScoreboard() { } private void generateOutput() { - try { log.info(" generateOutput() - create HTML "); - Properties scoringProperties = scoreboardCommon.getScoringProperties(getContest().getContestInformation().getScoringProperties()); - String saXML = algo.getStandings(getContest(), scoringProperties, log); + Properties scoringProperties = scoreboardCommon.getScoringProperties(contest.getContestInformation().getScoringProperties()); + String saXML = algo.getStandings(contest, scoringProperties, log); generateOutput(saXML); + ArrayList groupListOfOne = new ArrayList(); + for(Group group : contest.getGroups()) { + if(group.isDisplayOnScoreboard()) { + groupListOfOne.clear(); + groupListOfOne.add(group); + saXML = algo.getStandings(contest, null, null, groupListOfOne, scoringProperties, log); + generateOutput(saXML, group); + } + } } catch (Exception e) { log.log(Log.WARNING, "Exception generating scoreboard output " + e.getMessage(), e); } } private void generateOutput(String xmlString) { + generateOutput(xmlString, null); + } + + private void generateOutput(String xmlString, Group group) { String outputDir = contest.getContestInformation().getScoringProperties().getProperty(DefaultScoringAlgorithm.JUDGE_OUTPUT_DIR, "html"); - scoreboardCommon.generateOutput(xmlString, xslDir, outputDir, log); - scoreboardCommon.generateResults(contest, controller, xmlString, xslDir, log); + String groupName = null; + + if(group != null) { + groupName = group.getDisplayName(); + } + scoreboardCommon.generateOutput(xmlString, groupName, xslDir, outputDir, log); + scoreboardCommon.generateResults(contest, controller, xmlString, group, xslDir, log); try { Properties scoringProperties = scoreboardCommon.getScoringProperties(getContest().getContestInformation().getScoringProperties()); String frozenOutputDir = scoringProperties.getProperty(DefaultScoringAlgorithm.PUBLIC_OUTPUT_DIR); if (frozenOutputDir != null && frozenOutputDir.trim().length() > 0 && !frozenOutputDir.equals(outputDir)) { - String frozenXML = algoFrozen.getStandings(getContest(), scoringProperties, log); - scoreboardCommon.generateOutput(frozenXML, xslDir, frozenOutputDir, log); + ArrayList groupOfOneList = null; + if(group != null) { + groupOfOneList = new ArrayList(); + groupOfOneList.add(group); + } + String frozenXML = algoFrozen.getStandings(contest, null, null, groupOfOneList,scoringProperties, log); + scoreboardCommon.generateOutput(frozenXML, groupName, xslDir, frozenOutputDir, log); } } catch (Exception e) { log.warning("Exception generating frozen html"); @@ -157,24 +183,28 @@ protected boolean isThisSite(int siteNumber) { /** * Problem listener - * + * * @author ICPC * */ public class ProblemListenerImplementation implements IProblemListener { + @Override public void problemAdded(ProblemEvent event) { generateOutput(); } + @Override public void problemChanged(ProblemEvent event) { generateOutput(); } + @Override public void problemRemoved(ProblemEvent event) { generateOutput(); } + @Override public void problemRefreshAll(ProblemEvent event) { generateOutput(); } @@ -183,28 +213,33 @@ public void problemRefreshAll(ProblemEvent event) { /** * Account Listener - * + * * @author ICPC * */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { generateOutput(); } + @Override public void accountModified(AccountEvent event) { generateOutput(); } + @Override public void accountsAdded(AccountEvent accountEvent) { generateOutput(); } + @Override public void accountsModified(AccountEvent accountEvent) { generateOutput(); } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { generateOutput(); } @@ -212,20 +247,23 @@ public void accountsRefreshAll(AccountEvent accountEvent) { /** * ContestTime listener - * + * * @author ICPC * */ class ContestTimeListenerImplementation implements IContestTimeListener { + @Override public void contestTimeAdded(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void contestTimeRemoved(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void contestTimeChanged(ContestTimeEvent event) { ContestTime contestTime = event.getContestTime(); if (isThisSite(contestTime.getSiteNumber())) { @@ -233,14 +271,17 @@ public void contestTimeChanged(ContestTimeEvent event) { } } + @Override public void contestStarted(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void contestStopped(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void refreshAll(ContestTimeEvent event) { contestTimeChanged(event); } @@ -257,24 +298,28 @@ public void contestAutoStarted(ContestTimeEvent event) { } /** - * + * * @author pc2@ecs.csus.edu * */ public class RunListenerImplementation implements IRunListener { + @Override public void runAdded(RunEvent event) { generateOutput(); } + @Override public void refreshRuns(RunEvent event) { generateOutput(); } + @Override public void runChanged(RunEvent event) { generateOutput(); } + @Override public void runRemoved(RunEvent event) { generateOutput(); } @@ -282,24 +327,28 @@ public void runRemoved(RunEvent event) { /** * BalloonSettings listener - * + * * @author ICPC * */ public class BalloonSettingsListenerImplementation implements IBalloonSettingsListener { + @Override public void balloonSettingsAdded(BalloonSettingsEvent event) { generateOutput(); } + @Override public void balloonSettingsChanged(BalloonSettingsEvent event) { generateOutput(); } + @Override public void balloonSettingsRemoved(BalloonSettingsEvent event) { generateOutput(); } + @Override public void balloonSettingsRefreshAll(BalloonSettingsEvent balloonSettingsEvent) { generateOutput(); } @@ -307,30 +356,35 @@ public void balloonSettingsRefreshAll(BalloonSettingsEvent balloonSettingsEvent) /** * a ContestInformation Listener - * + * * @author ICPC * */ class ContestInformationListenerImplementation implements IContestInformationListener { + @Override public void contestInformationAdded(ContestInformationEvent event) { generateOutput(); } + @Override public void contestInformationChanged(ContestInformationEvent event) { generateOutput(); } + @Override public void contestInformationRemoved(ContestInformationEvent event) { // ignored } + @Override public void contestInformationRefreshAll(ContestInformationEvent contestInformationEvent) { generateOutput(); } + @Override public void finalizeDataChanged(ContestInformationEvent contestInformationEvent) { generateOutput(); } @@ -360,7 +414,7 @@ public IInternalController getController() { /** * a Language listener - * + * * @author ICPC * */ diff --git a/src/edu/csus/ecs/pc2/ui/board/ScoreboardView.java b/src/edu/csus/ecs/pc2/ui/board/ScoreboardView.java index b51ae951e..8bd9c2cf9 100644 --- a/src/edu/csus/ecs/pc2/ui/board/ScoreboardView.java +++ b/src/edu/csus/ecs/pc2/ui/board/ScoreboardView.java @@ -1,10 +1,11 @@ -// Copyright (C) 1989-2023 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.board; import java.awt.BorderLayout; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; +import java.util.ArrayList; import java.util.Properties; import javax.swing.JButton; @@ -24,6 +25,7 @@ import edu.csus.ecs.pc2.core.log.StaticLog; import edu.csus.ecs.pc2.core.model.ContestTime; import edu.csus.ecs.pc2.core.model.ContestTimeEvent; +import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IContestTimeListener; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.scoring.DefaultScoringAlgorithm; @@ -43,14 +45,14 @@ /** * This class is the default scoreboard view (frame). - * + * * @author pc2@ecs.csus.edu */ public class ScoreboardView extends JFrame implements UIPlugin { /** - * + * */ private static final long serialVersionUID = -8071477348056424178L; @@ -83,9 +85,9 @@ public class ScoreboardView extends JFrame implements UIPlugin { private JPanel clockPanel = null; private ScoreboardCommon scoreboardCommon = new ScoreboardCommon(); - + private DefaultScoringAlgorithm algo = new DefaultScoringAlgorithm(); - + /* * We set setObeyFrozen = true on this one. */ @@ -93,7 +95,7 @@ public class ScoreboardView extends JFrame implements UIPlugin { /** * This method initializes - * + * */ public ScoreboardView() { super(); @@ -101,7 +103,7 @@ public ScoreboardView() { } /** - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -109,12 +111,16 @@ public ScoreboardView() { // $HeadURL$ public class PropertyChangeListenerImplementation implements PropertyChangeListener { + @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equalsIgnoreCase("standings")) { if (evt.getNewValue() != null && !evt.getNewValue().equals(evt.getOldValue())) { // standings have changed // TODO take this off the awt thread - generateOutput((String) evt.getNewValue()); +// generateOutput((String) evt.getNewValue()); + // we no longer use the XML passed in since we have no idea what it was for (which group?) + // rather, we just recompute all the boards + generateOutput(); } } } @@ -122,7 +128,7 @@ public void propertyChange(PropertyChangeEvent evt) { /** * This method initializes this - * + * */ private void initialize() { this.setSize(new java.awt.Dimension(800, 450)); @@ -131,6 +137,7 @@ private void initialize() { this.setTitle("Scoreboard"); this.addWindowListener(new java.awt.event.WindowAdapter() { + @Override public void windowClosing(java.awt.event.WindowEvent e) { promptAndExit(); } @@ -160,6 +167,7 @@ protected void promptAndExit() { } } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { this.contest = inContest; this.controller = inController; @@ -182,6 +190,7 @@ public void setContestAndController(IInternalContest inContest, IInternalControl controller.register(contestClockDisplay); SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { setTitle("PC^2 " + contest.getTitle() + " Build " + new VersionInfo().getBuildNumber()); @@ -192,7 +201,7 @@ public void run() { StandingsTablePane standingsTablePane = new StandingsTablePane(); addUIPlugin(getMainTabbedPane(), "Standings", standingsTablePane); standingsTablePane.addPropertyChangeListener("standings", new PropertyChangeListenerImplementation()); - + NSAStandingsPane nsaStandingsPane = new NSAStandingsPane(); addUIPlugin(getMainTabbedPane(), "Standings New", nsaStandingsPane); @@ -238,6 +247,7 @@ public void run() { }); } + @Override public String getPluginTitle() { return "Scoreboard View"; } @@ -248,7 +258,7 @@ protected void addUIPlugin(JTabbedPane tabbedPane, String tabTitle, JPanePlugin plugin.setContestAndController(contest, controller); tabbedPane.add(plugin, tabTitle); } - + private void generateOutput() { try { @@ -256,21 +266,44 @@ private void generateOutput() { Properties scoringProperties = scoreboardCommon.getScoringProperties(contest.getContestInformation().getScoringProperties()); String saXML = algo.getStandings(contest, scoringProperties, log); generateOutput(saXML); + ArrayList groupListOfOne = new ArrayList(); + for(Group group : contest.getGroups()) { + if(group.isDisplayOnScoreboard()) { + groupListOfOne.clear(); + groupListOfOne.add(group); + saXML = algo.getStandings(contest, null, null, groupListOfOne, scoringProperties, log); + generateOutput(saXML, group); + } + } } catch (Exception e) { log.log(Log.WARNING, "Exception generating scoreboard output " + e.getMessage(), e); } } private void generateOutput(String xmlString) { + generateOutput(xmlString, null); + } + + private void generateOutput(String xmlString, Group group) { String outputDir = contest.getContestInformation().getScoringProperties().getProperty(DefaultScoringAlgorithm.JUDGE_OUTPUT_DIR, "html"); - scoreboardCommon.generateOutput(xmlString, xslDir, outputDir, log); - scoreboardCommon.generateResults(contest, controller, xmlString, xslDir, log); + String groupName = null; + + if(group != null) { + groupName = group.getDisplayName(); + } + scoreboardCommon.generateOutput(xmlString, groupName, xslDir, outputDir, log); + scoreboardCommon.generateResults(contest, controller, xmlString, group, xslDir, log); try { String frozenOutputDir = contest.getContestInformation().getScoringProperties().getProperty(DefaultScoringAlgorithm.PUBLIC_OUTPUT_DIR); if (frozenOutputDir != null && frozenOutputDir.trim().length() > 0 && !frozenOutputDir.equals(outputDir)) { Properties scoringProperties = scoreboardCommon.getScoringProperties(contest.getContestInformation().getScoringProperties()); - String frozenXML = algoFrozen.getStandings(contest, scoringProperties, log); - scoreboardCommon.generateOutput(frozenXML, xslDir, frozenOutputDir, log); + ArrayList groupOfOneList = null; + if(group != null) { + groupOfOneList = new ArrayList(); + groupOfOneList.add(group); + } + String frozenXML = algoFrozen.getStandings(contest, null, null, groupOfOneList,scoringProperties, log); + scoreboardCommon.generateOutput(frozenXML, groupName, xslDir, frozenOutputDir, log); } } catch (Exception e) { log.warning("Exception generating frozen html"); @@ -280,7 +313,7 @@ private void generateOutput(String xmlString) { /** * This method initializes mainTabbedPane - * + * * @return javax.swing.JTabbedPane */ private JTabbedPane getMainTabbedPane() { @@ -292,7 +325,7 @@ private JTabbedPane getMainTabbedPane() { /** * This method initializes mainViewPane - * + * * @return javax.swing.JPanel */ private JPanel getMainViewPane() { @@ -307,7 +340,7 @@ private JPanel getMainViewPane() { /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getNorthPane() { @@ -331,7 +364,7 @@ private JPanel getNorthPane() { /** * This method initializes jPanel1 - * + * * @return javax.swing.JPanel */ private JPanel getEastPane() { @@ -345,7 +378,7 @@ private JPanel getEastPane() { /** * This method initializes jButton - * + * * @return javax.swing.JButton */ private JButton getExitButton() { @@ -355,6 +388,7 @@ private JButton getExitButton() { exitButton.setToolTipText("Click here to Shutdown PC^2"); exitButton.setMnemonic(java.awt.event.KeyEvent.VK_X); exitButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { promptAndExit(); } @@ -366,6 +400,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { private void setFrameTitle(final boolean contestStarted) { final JFrame thisFrame = this; SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { FrameUtilities.setFrameTitle(thisFrame, contest.getTitle(), contestStarted, new VersionInfo()); @@ -389,21 +424,24 @@ protected boolean isThisSite(int siteNumber) { } /** - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ class ContestTimeListenerImplementation implements IContestTimeListener { + @Override public void contestTimeAdded(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void contestTimeRemoved(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void contestTimeChanged(ContestTimeEvent event) { ContestTime contestTime = event.getContestTime(); if (isThisSite(contestTime.getSiteNumber())) { @@ -411,14 +449,17 @@ public void contestTimeChanged(ContestTimeEvent event) { } } + @Override public void contestStarted(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void contestStopped(ContestTimeEvent event) { contestTimeChanged(event); } + @Override public void refreshAll(ContestTimeEvent event) { contestTimeChanged(event); } @@ -437,6 +478,7 @@ public void contestAutoStarted(ContestTimeEvent event) { private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); messageLabel.setToolTipText(string); @@ -447,7 +489,7 @@ public void run() { /** * This method initializes refreshButton - * + * * @return javax.swing.JButton */ private JButton getRefreshButton() { @@ -458,8 +500,10 @@ private JButton getRefreshButton() { refreshButton.setMnemonic(java.awt.event.KeyEvent.VK_R); refreshButton.setText("Refresh"); refreshButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { new Thread(new Runnable() { + @Override public void run() { generateOutput(); } @@ -482,7 +526,7 @@ private void logException(Exception e) { /** * This method initializes clockPanel - * + * * @return javax.swing.JPanel */ private JPanel getClockPanel() { diff --git a/src/edu/csus/ecs/pc2/ui/team/CreateTeamsTSV.java b/src/edu/csus/ecs/pc2/ui/team/CreateTeamsTSV.java index 88c80effb..1b715f1d6 100644 --- a/src/edu/csus/ecs/pc2/ui/team/CreateTeamsTSV.java +++ b/src/edu/csus/ecs/pc2/ui/team/CreateTeamsTSV.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2021 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.team; import java.io.File; @@ -22,7 +22,7 @@ /** * Read teams.json write teams.tsv - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -38,9 +38,9 @@ public class CreateTeamsTSV { public static final String TEAMS_JSON_FILENAME = "teams.json"; private static final String OUTPUT_FILE_KEY = "--of"; - + private static final String INPUT_FILE_KEY = "--if"; - + private static final String OVERWRITE_FILE_KEY = "-f"; /** @@ -55,7 +55,7 @@ public class CreateTeamsTSV { private static final int FAILURE_EXIT_CODE = 4; private boolean debugMode = false; - + private boolean allowOverWrite = false; /** @@ -92,10 +92,10 @@ protected void loadVariables(String[] args) throws CommandLineErrorException { } debugMode = arguments.isOptPresent("--debug"); - - + + allowOverWrite = arguments.isOptPresent(OVERWRITE_FILE_KEY); - + if (debugMode) { arguments.dumpArgs(System.err); @@ -104,11 +104,11 @@ protected void loadVariables(String[] args) throws CommandLineErrorException { if (arguments.isOptPresent(OUTPUT_FILE_KEY)) { outputFileName = arguments.getOptValue(OUTPUT_FILE_KEY); } - + if (arguments.isOptPresent(INPUT_FILE_KEY)) { inputFileName = arguments.getOptValue(INPUT_FILE_KEY); } - + } @@ -162,17 +162,19 @@ protected void writeTSVFile() throws Exception { // TODO write teams.tsv output file // System.out.println(" debug 22 " + teamAccount.toJSON()); + // No special support for multiple groups as this is a legacy feed and specifies + // only one group String groupId = "1"; if (teamAccount.getGroup_ids().size() > 0) { groupId = teamAccount.getGroup_ids().get(0); } - + Integer value = groupMap.get(groupId); if (value == null) { value = 0; } groupMap.put(groupId, value.intValue()+1); - + /** 1 Team Number 22 integer 2 External ID 24314 integer @@ -180,7 +182,7 @@ protected void writeTSVFile() throws Exception { 4 Team name Hoos string 5 Institution name University of Virginia string 6 Institution short name U Virginia string -7 Country Code USA string ISO 3166-1 alpha-3 +7 Country Code USA string ISO 3166-1 alpha-3 */ String [] fields = { // ""+teamAccount.getId(), @@ -205,14 +207,14 @@ protected List loadAccounts(String jsonFilename) throws JsonParseEx if (! new File(jsonFilename).exists()) { throw new FileNotFoundException(jsonFilename); } - + ObjectMapper mapper = new ObjectMapper(); String[] lines = Utilities.loadFile(jsonFilename); List list = Arrays.asList(mapper.readValue(lines[0], TeamAccount[].class)); return list; } - + protected void updateGroupsTSV() throws IOException { String outDir = new File(outputFileName).getParent(); @@ -221,28 +223,28 @@ protected void updateGroupsTSV() throws IOException { } String outGroupName = outDir + File.separator + GROUPS_TSV_FILENAME; FileWriter writer = (new FileWriter(new File(outGroupName))); - + String[] groupHeaerFields = { "groups", "1" }; writer.write(String.join(TAB, groupHeaerFields) + NL); Set groupIds = groupMap.keySet(); - String[] ids = (String[]) groupIds.toArray(new String[groupIds.size()]); + String[] ids = groupIds.toArray(new String[groupIds.size()]); Arrays.sort(ids); - + for (String id : ids) { writer.write(id + TAB + "Group " + id + NL); } - + writer.close(); writer = null; } - + public String getOutputFileName() { return outputFileName; } - + public String getInputFileName() { return inputFileName; } @@ -264,5 +266,5 @@ public static void main(String[] args) { } } - + } diff --git a/src/edu/csus/ecs/pc2/util/ScoreboardVariableReplacer.java b/src/edu/csus/ecs/pc2/util/ScoreboardVariableReplacer.java index cd79a9ef7..4e392e542 100644 --- a/src/edu/csus/ecs/pc2/util/ScoreboardVariableReplacer.java +++ b/src/edu/csus/ecs/pc2/util/ScoreboardVariableReplacer.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2021 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.util; import edu.csus.ecs.pc2.core.CommandVariableReplacer; @@ -9,7 +9,7 @@ /** * Substitute scoreboard team values for variables. - * + * * @author Douglas A. Lane * */ @@ -50,10 +50,10 @@ public class ScoreboardVariableReplacer { /** * Substitutes values for variables. - * - * + * + * * Variable names and examples. - * + * *
          * Client/Team number - {:clientnumber} = 514
          * Country Code - {:countrycode} = CAN
    @@ -66,7 +66,7 @@ public class ScoreboardVariableReplacer {
          * Team login name - {:teamloginname} = team514
          * Team name - {:teamname} = UBC!
          * 
    - * + * * @param origString original string with variables * @param account team account * @param group team group @@ -109,17 +109,21 @@ public static String substituteDisplayNameVariables(String origString, Account a } /** - * + * * @see #substituteDisplayNameVariables(String, Account, Group) - * + * * @param origString * @param contest * @param account * @return */ public static String substituteDisplayNameVariables(String origString, IInternalContest contest, Account account) { - if (account.getGroupId() != null) { - return substituteDisplayNameVariables(origString, account, contest.getGroup(account.getGroupId())); + // it is probably OK to use the "primary" group ID here (the one supplied by the CMS). + // this is used to augment the teamName for display. Using the CMS group should convey + // the desired information: eg. Hawaii - D2 (for example). Would we want to just show "D2" or "Hawaii" ? + // probably not - we want the compound group name (eg CMS name). + if (account.getPrimaryGroupId() != null) { + return substituteDisplayNameVariables(origString, account, contest.getGroup(account.getPrimaryGroupId())); } else { return substituteDisplayNameVariables(origString, account, null); } diff --git a/test/edu/csus/ecs/pc2/api/APISampleCode.java b/test/edu/csus/ecs/pc2/api/APISampleCode.java index 374e28c0f..f12f6bd4f 100644 --- a/test/edu/csus/ecs/pc2/api/APISampleCode.java +++ b/test/edu/csus/ecs/pc2/api/APISampleCode.java @@ -1,14 +1,17 @@ -// 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.api; +import java.util.HashMap; + import edu.csus.ecs.pc2.api.exceptions.LoginFailureException; import edu.csus.ecs.pc2.api.exceptions.NotLoggedInException; +import edu.csus.ecs.pc2.core.model.ElementId; /** * Sample Code for API. - * + * * This class is not intended as a JUnit test, it is a syntax check for the API samples in the Java doc in the API classes. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -42,7 +45,7 @@ public void serverConnectionSample() { /** * getTeams() sample. - * + * * @param contest The contest from which team samples are to be drawn */ public void getTeamsSample(IContest contest) { @@ -50,15 +53,32 @@ public void getTeamsSample(IContest contest) { for (ITeam team : contest.getTeams()) { String teamName = team.getDisplayName(); int siteNumber = team.getSiteNumber(); - String groupName = team.getGroup().getName(); - System.out.println(teamName + " Site: " + siteNumber + " Group: " + groupName); + + HashMap groups = team.getGroups(); + String groupName = ""; + boolean first = true; + for(ElementId groupElementId : groups.keySet()) { + IGroup group = groups.get(groupElementId); + if(group != null) { + if(first) { + first = false; + } else { + groupName = groupName + ","; + } + groupName = groupName + group.getName(); + } + } + if(groupName.isEmpty()) { + groupName = "(no groups assigned)"; + } + System.out.println(teamName + " Site: " + siteNumber + " Groups: " + groupName); } } /** * getLanguages() sample. - * + * * @param contest The contest from which language samples are to be drawn */ public void getLanguagesSample(IContest contest) { @@ -71,7 +91,7 @@ public void getLanguagesSample(IContest contest) { /** * getProblems() sample. - * + * * @param contest The contest from which problem samples are to be drawn */ public void getProblemSample(IContest contest) { @@ -84,7 +104,7 @@ public void getProblemSample(IContest contest) { /** * getJudgements() sample. - * + * * @param contest The contest from which judgement samples are to be drawn */ public void getJudgmentsSample(IContest contest) { @@ -97,7 +117,7 @@ public void getJudgmentsSample(IContest contest) { /** * getRuns() sample. - * + * * @param contest The contest from which run samples are to be drawn */ public void getRunsSample(IContest contest) { @@ -119,7 +139,7 @@ public void getRunsSample(IContest contest) { /** * getStandings() samples. - * + * * @param contest The contest from which Standings samples are to be drawn */ public void getStandingsSample(IContest contest) { diff --git a/test/edu/csus/ecs/pc2/api/implementation/ContestTest.java b/test/edu/csus/ecs/pc2/api/implementation/ContestTest.java index 4a672162e..db63b9250 100644 --- a/test/edu/csus/ecs/pc2/api/implementation/ContestTest.java +++ b/test/edu/csus/ecs/pc2/api/implementation/ContestTest.java @@ -1,4 +1,4 @@ -// 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.api.implementation; import java.io.File; @@ -39,7 +39,7 @@ /** * API Unit test. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -76,7 +76,7 @@ public void testProblems() throws Exception { Contest apiContestInst = new Contest(contest, controller, log); IContest apiContest = apiContestInst; - + Problem[] problems = contest.getProblems(); assertNotNull("Expecting problems ", problems); assertEquals("expected problems count", 6, problems.length); @@ -95,7 +95,7 @@ public void testProblems() throws Exception { } } } - + public void testProblemDetails() throws Exception { IInternalContest contest = sampleContest.createContest(1, 3, 12, 12, true); @@ -149,7 +149,7 @@ public void testProblemDetails() throws Exception { IProblem[] problemsApi = apiContest.getProblems(); IProblem[] problemsApiAll = apiContest.getAllProblems(); - // Test for Bug + // Test for Bug assertEquals("Same problems for API ", problems.length - 1, problemsApi.length); assertTrue("Expecting problem " + problemtoDelete + " to be deleted ", problemsApiAll[problemtoDelete].isDeleted()); @@ -166,37 +166,37 @@ protected void dumpDetails(IContest contest, IProblemDetails[] details) { println(" " + detailCounter + " " + problems[det.getProblemId() - 1].getName() + // " solved=" + solved + " solutionTime=" + det.getSolutionTime() + // " points=" + det.getPenaltyPoints() + " attempts=" + det.getAttempts()+" "+det.getClient().getLoginName()); - + } } - + /** * Tests for Bug 766 - Add support for general problem/problem categories. - * + * * @throws Exception */ public void testProblemClars() throws Exception { IContest apiContest = createInstance("tpc"); - + IProblem[] cats = apiContest.getClarificationCategories(); - + assertEquals("Expecting one category ", 1, cats.length); Problem prob = sampleContest.getGeneralProblem(); assertEquals("Expecting general ", prob.getDisplayName(), cats[0].getName()); - + } private void println(String string) { System.out.println(string); - + } - + private int countClients(IClient [] list, int siteNumber, IClient.ClientType type){ - + int count = 0; - + for (IClient iClient : list) { if (iClient.getSiteNumber() == siteNumber){ if (iClient.getType().equals(type)){ @@ -204,14 +204,14 @@ private int countClients(IClient [] list, int siteNumber, IClient.ClientType typ } } } - + return count; } - + public void testGetClients() throws Exception { - + IContest contest = createInstance("testGetClients"); - + ITeam[] teams = contest.getTeams(); assertEquals("Expecting teams ", 12, teams.length); @@ -237,22 +237,22 @@ public void testGetClients() throws Exception { number = countClients(allClients, 1, ClientType.SCOREBOARD_CLIENT); assertEquals("Scoreboard clients ", 1, number); } - + public void testRunStatus() throws Exception { - + String [] runsData = { "1,1,A,1,No", //20 "2,1,A,3,Yes", //3 (first yes counts Minutes only) "3,1,A,5,No", //20 - "4,1,A,7,Yes", //20 + "4,1,A,7,Yes", //20 "5,1,A,9,No", //20 - + "6,1,B,11,No", //20 (all runs count) "7,1,B,13,No", //20 (all runs count) - + "8,2,A,30,Yes", //30 - + "9,2,B,35,No", //20 (all runs count) "10,2,B,40,No", //20 (all runs count) "11,2,B,45,No", //20 (all runs count) @@ -264,7 +264,7 @@ public void testRunStatus() throws Exception { "16,2,A,330, ", // doesn't count, yes after yes }; - + IInternalContest contest = sampleContest.createContest(1, 3, 12, 12, true); ensureOutputDirectory(); @@ -274,18 +274,18 @@ public void testRunStatus() throws Exception { Log log = createLog("testRunStatus" + getName()); Contest apiContestInst = new Contest(contest, controller, log); - + for (String runInfoLine : runsData) { - sampleContest.addARun(contest, runInfoLine); + sampleContest.addARun(contest, runInfoLine); } - + IRun[] runs = apiContestInst.getRuns(); - + assertEquals("Number of runs", 16, runs.length); - + assertEquals("Number of NEW runs", 3, countRunStatus(apiContestInst, runs, RunStates.NEW)); assertEquals("Number of JUDGED runs", 13, countRunStatus(apiContestInst, runs, RunStates.JUDGED)); - + // for (IRun iRun : runs) { // println("debug "+apiContestInst.getRunState(iRun)); // } @@ -301,45 +301,45 @@ private int countRunStatus(Contest contest, IRun[] runs, RunStates runStates) { return count; } - + public void testgetVersionParts() throws Exception { IContest apiContest = createInstance("tgparts"); - + String[] data = { // input,expected "9.3Beta,9+3+Beta", // "2.2,2+2+", // "2.,2.++", // - + }; for (String line : data) { String[] fields = line.trim().split(","); String input = fields[0]; String expected = fields[1]; - + String [] results = ((Contest)apiContest).getVersionParts(input); String actual = join("+", results); - + // println("\""+input+","+actual+"\", //"); - + assertEquals("Expected matching strings", expected, actual); } - + // String s = apiContest.getBuildNumber(); // println("Build number: "+s); // s = apiContest.getMajorVersion(); // println("Major : "+s); // s = apiContest.getMinorVersion(); // println("Minor: "+s); - - - + + + } - + public void testGetRun() throws Exception { - + IInternalContest contest = sampleContest.createContest(1, 3, 12, 12, true); assertEquals("Site id", 1, contest.getSiteNumber()); @@ -367,40 +367,40 @@ public void testGetRun() throws Exception { assertEquals("Expecting runs ", expectedNumberOfRuns, runs.length); // Edge Tests - + IRun runOne = apiContest.getRun(1); assertNotNull("Expecting run id 1 ", runOne); assertEquals("Run id", 1, runOne.getNumber()); - + int lastRunNumber = runs.length; IRun lastRun = apiContest.getRun(lastRunNumber); assertNotNull("Expecting run id "+lastRun, lastRun); assertEquals("Run id", lastRunNumber, lastRun.getNumber()); - + // out of upper range test - + IRun lastRunPlusOne = apiContest.getRun(lastRunNumber+1); assertNull("Expecting null for run id "+(lastRunNumber+1), lastRunPlusOne); - + // negative test, ahem - + int runId = -3; IRun run = apiContest.getRun(-3); assertNull("Expecting null for run id "+runId, run); - + // mid point test - + runId = runs.length / 2; run = apiContest.getRun(runId); assertNotNull("Expecting to find run id "+runId, run); assertEquals("Run id", runId, run.getNumber()); - + } - + public void testLanguagesImpl() throws Exception { - + Language language = LanguageAutoFill.createAutoFilledLanguage(LanguageAutoFill.JAVATITLE); - + IInternalContest internal = sampleContest.createContest(1, 3, 12, 12, true); ensureOutputDirectory(); @@ -408,46 +408,46 @@ public void testLanguagesImpl() throws Exception { IInternalController controller = sampleContest.createController(internal, storageDirectory, true, false); Log log = createLog("loggy" + getName()); - + internal.addLanguage(language); - + Contest contest = new Contest(internal, controller, log); - + int lastIndex = contest.getLanguages().length - 1; ILanguage lang = contest.getLanguages()[lastIndex]; - + assertEquals("getName ", language.getDisplayName(), lang.getName()); assertEquals("getTitle ", language.getDisplayName(), lang.getTitle()); - + assertEquals("getCompileCommandLine ", language.getCompileCommandLine(), lang.getCompilerCommandLine()); assertEquals("isInterpreted ", language.isInterpreted(), lang.isInterpreted()); assertEquals("getExecutableMask ", language.getExecutableIdentifierMask(), lang.getExecutableMask()); assertEquals("getExecutionCommandLine ", language.getProgramExecuteCommandLine(), lang.getExecutionCommandLine()); - + assertFalse("isInterpreted ", lang.isInterpreted()); - + language = LanguageAutoFill.createAutoFilledLanguage(LanguageAutoFill.PHPTITLE); - + internal.addLanguage(language); - + lastIndex = contest.getLanguages().length - 1; lang = contest.getLanguages()[lastIndex]; - + assertEquals("getName ", language.getDisplayName(), lang.getName()); assertEquals("getTitle ", language.getDisplayName(), lang.getTitle()); - + assertEquals("getCompileCommandLine ", language.getCompileCommandLine(), lang.getCompilerCommandLine()); assertEquals("isInterpreted ", language.isInterpreted(), lang.isInterpreted()); - + assertEquals("getExecutableMask ", language.getExecutableIdentifierMask(), lang.getExecutableMask()); assertEquals("getExecutionCommandLine ", language.getProgramExecuteCommandLine(), lang.getExecutionCommandLine()); - + assertTrue("isInterpreted ", lang.isInterpreted()); - + } - + // TODO REFACTOR move to AbstractTestcase private IInternalContest loadSampleContest(IInternalContest contest, String sampleName) throws Exception { @@ -486,7 +486,7 @@ public void testAPIBroadcastClars() throws Exception { Account team = getTeamAccounts(internal)[0]; - assertNotNull("Team " + team.getClientId() + " not assigned a group", team.getGroupId()); + assertNotNull("Team " + team.getClientId() + " not assigned a group", team.getGroupIds()); int addedClarDcount = addClarification(internal, team, internal.getProblems(), "Which team is? "); assertEquals("added clar count", 6, addedClarDcount); @@ -500,33 +500,33 @@ public void testAPIBroadcastClars() throws Exception { Group[] groups = internal.getGroups(); assertEquals("Expecting group count ", 12, groups.length); - + // Create Answered clar from Division 1 - + Account div1team = internal.getAccount(new ClientId(1, Type.TEAM, 301)); assertNotNull(div1team); // 301 308430 12546 Lute Octothorpe Lute Octothorpe (PLU) PLU USA - + assertEquals("Team 301 display name", "Lute Octothorpe (PLU)", div1team.getDisplayName()); Account judgeAccount = internal.getAccounts(Type.JUDGE).firstElement(); - + Problem problem = internal.getProblems()[0]; - + Clarification answerClar = createBroadcastClar(internal, div1team, problem, "Broadcast clar", "Answer is here!", judgeAccount.getClientId()); - + IClarification[] clars = contest.getClarifications(); - + assertEquals("Expected clars from API", 7, clars.length); // Failes on getClarification if bug not fixed IClarification[] allClars = contest.getClarifications(); assertNotNull(allClars); } - + /** * Created send to all clar. - * + * * @param internal * @param team * @param problem @@ -535,25 +535,25 @@ public void testAPIBroadcastClars() throws Exception { * @param whoAnsweredIt * @return answered clarification */ - private Clarification createBroadcastClar(IInternalContest internal, Account team, Problem problem, String quetion, String answer, ClientId whoAnsweredIt) { - Clarification clarification = new Clarification(team.getClientId(), problem, quetion + " from " + team.getClientId() + " group " + team.getGroupId()); + private Clarification createBroadcastClar(IInternalContest internal, Account team, Problem problem, String question, String answer, ClientId whoAnsweredIt) { + Clarification clarification = new Clarification(team.getClientId(), problem, question + " from " + team.getClientId() + " groups " + team.getGroupIds()); Clarification newClar = internal.acceptClarification(clarification); internal.answerClarification(newClar, answer, whoAnsweredIt, true); return internal.getClarification(newClar.getElementId()); } public static void dumpTeamAccounts(String message, IInternalContest internal, int max) { - + Account[] teams = getTeamAccounts(internal); System.out.println("dumpTeamAccounts " + message); - + int cnt = 0; - + for (Account account : teams) { System.out.println(account.getClientId().getName()+" name="+ account.getDisplayName() + " " +// account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD) + " " + // - account.getGroupId() + " " + // + account.getGroupIds() + " " + // account.getInstitutionCode() + " " + // account.getExternalId() + " " + // "" @@ -562,22 +562,22 @@ public static void dumpTeamAccounts(String message, IInternalContest internal, i break; } cnt ++; - + } System.out.println("dumpTeamAccounts " + message+ " end team count="+teams.length); } - + public static void dumpTeamAccounts(String message, Account[] teams , int max) { - + System.out.println("dumpTeamAccounts " + message); - + int cnt = 0; - + for (Account account : teams) { System.out.println(account.getClientId().getName()+" name="+ account.getDisplayName() + " " +// account.isAllowed(Permission.Type.DISPLAY_ON_SCOREBOARD) + " " + // - account.getGroupId() + " " + // + account.getGroupIds() + " " + // account.getInstitutionCode() + " " + // account.getExternalId() + " " + // "" @@ -586,7 +586,7 @@ public static void dumpTeamAccounts(String message, Account[] teams , int max) { break; } cnt ++; - + } System.out.println("dumpTeamAccounts " + message+ " end team count="+teams.length); @@ -595,19 +595,19 @@ public static void dumpTeamAccounts(String message, Account[] teams , int max) { private int addClarification(IInternalContest internal, Account team, Problem[] problems, String content) { for (Problem problem : problems) { - Clarification clarification = new Clarification(team.getClientId(), problem, content + " from " + team.getClientId() + " group " + team.getGroupId()); + Clarification clarification = new Clarification(team.getClientId(), problem, content + " from " + team.getClientId() + " group " + team.getGroupIds()); internal.acceptClarification(clarification); } return problems.length; } - + /** - * + * * Unit test for: Issue Broadcast Clars "to all teams" don't show up in WTI * https://github.com/pc2ccs/pc2v9/issues/186 - * + * * @throws Exception */ public void NONtestAPIGEtBroadcastClarLive() throws Exception { @@ -616,14 +616,14 @@ public void NONtestAPIGEtBroadcastClarLive() throws Exception { //Start server with teams from different divisions, like sample mini contest //Submit clars from team 151 (D2) //judge answers clars check send to all - // + // // 301 308430 12546 Lute Octothorpe Lute Octothorpe (PLU) PLU USA // 151 309101 312543 D2 Eagle White D2 Eagle White (EWU) EWU USA - + ServerConnection serverConnection = new ServerConnection(); String testId = "team301"; // D1 team // testId = "team151"; // D2 team - + // If server not started will throw LoginFailureException: Unable to contact server at localhost:50002 (server not started?) IContest contest = serverConnection.login(testId, testId); @@ -633,7 +633,7 @@ public void NONtestAPIGEtBroadcastClarLive() throws Exception { // for (IProblem iProblem : probs) { // System.out.println("Prob " + iProblem.getName()); // } - + assertEquals("number of problems D2", 4, probs.length); // D1 // assertEquals("number of problems D1", 2, probs.length); // D2 @@ -641,7 +641,7 @@ public void NONtestAPIGEtBroadcastClarLive() throws Exception { // java.lang.NullPointerException // at edu.csus.ecs.pc2.api.implementation.ProblemImplementation.(ProblemImplementation.java:52) // at edu.csus.ecs.pc2.api.implementation.ProblemImplementation.(ProblemImplementation.java:48) - + IClarification[] clars = contest.getClarifications(); for (IClarification clar : clars) { @@ -650,38 +650,38 @@ public void NONtestAPIGEtBroadcastClarLive() throws Exception { } } - + /** * Test login to server. * @throws Exception */ public void NOTtestLogin() throws Exception { - + ServerConnection serverConnection = new ServerConnection(); String testId = "team92"; testId = "team303"; IContest contest = serverConnection.login(testId, testId); - + assertNotNull(contest); - + } - + public void NOTtestGetProblemName(){ - + IInternalContest contest = sampleContest.createStandardContest(); - + Problem[] problems = contest.getProblems(); - + for (Problem problem : problems) { String stripped = ProblemImplementation. getProblemName(problem.getElementId()); println("Problem name = " + problem.getDisplayName() + " ele = " + problem.getElementId()+" newstr '"+stripped+"'"); } - + } - + /** * Test IVersionInfo. - * + * * @throws Exception */ public void testGetIVersionInfo() throws Exception { @@ -708,5 +708,5 @@ public void testGetIVersionInfo() throws Exception { // System.out.println("debug "+JSONUtilities.prettyPrint(verinfo)); // System.out.println("debug " + version info = "+verinfo); } - + } diff --git a/test/edu/csus/ecs/pc2/api/implementation/TeamImplementationTest.java b/test/edu/csus/ecs/pc2/api/implementation/TeamImplementationTest.java index 9ad2fd5b6..3f7b07a64 100644 --- a/test/edu/csus/ecs/pc2/api/implementation/TeamImplementationTest.java +++ b/test/edu/csus/ecs/pc2/api/implementation/TeamImplementationTest.java @@ -1,23 +1,23 @@ -// 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.api.implementation; import java.util.Arrays; -import junit.framework.TestCase; import edu.csus.ecs.pc2.api.ITeam; import edu.csus.ecs.pc2.core.list.AccountComparator; import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; +import edu.csus.ecs.pc2.core.model.ClientType.Type; import edu.csus.ecs.pc2.core.model.ContestTime; import edu.csus.ecs.pc2.core.model.Group; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.SampleContest; -import edu.csus.ecs.pc2.core.model.ClientType.Type; +import junit.framework.TestCase; /** * JUnit test for API TeamImplementation. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -30,7 +30,7 @@ private Account getAccount(IInternalContest contest, ClientType.Type type, int a if (siteNumber == 0) { siteNumber = 1; } - Account[] accounts = (Account[]) contest.getAccounts(type, siteNumber).toArray(new Account[contest.getAccounts(type, siteNumber).size()]); + Account[] accounts = contest.getAccounts(type, siteNumber).toArray(new Account[contest.getAccounts(type, siteNumber).size()]); Arrays.sort(accounts, new AccountComparator()); return accounts[accountNumber]; } @@ -59,7 +59,7 @@ public void testGetLoginName() { IInternalContest contest = createContest(); Account account = getAccount(contest,Type.TEAM, 3); ITeam team = new TeamImplementation(account, contest); - + assertEquals(account.getClientId().getName(), team.getLoginName()); } @@ -70,7 +70,7 @@ public void testGetDisplayName() { IInternalContest contest = createContest(); Account account = getAccount(contest,Type.TEAM, 3); ITeam team = new TeamImplementation(account, contest); - + assertEquals(account.getDisplayName(), team.getDisplayName()); } @@ -81,18 +81,19 @@ public void testGetGroup() { IInternalContest contest = createContest(); Account account = getAccount(contest,Type.TEAM, 3); ITeam team = new TeamImplementation(account, contest); - - assertNull("Expected Group null for "+account.getClientId().getTripletKey(),team.getGroup()); - + + assertNull("Expected Group null for "+account.getClientId().getTripletKey(),team.getPrimaryGroup()); + // Add and assign group - + Group group = new Group("Le Group"); contest.addGroup(group); - account.setGroupId(group.getElementId()); + account.clearGroups(); + account.addGroupId(group.getElementId(), true); ITeam teamWithGroup= new TeamImplementation(account, contest); - - assertNotNull("Expected non-null Group for "+account.getClientId().getTripletKey(),teamWithGroup.getGroup()); - - assertEquals(group.getDisplayName(), teamWithGroup.getGroup().getName()); + + assertNotNull("Expected non-null Group for "+account.getClientId().getTripletKey(),teamWithGroup.getPrimaryGroup()); + + assertEquals(group.getDisplayName(), teamWithGroup.getPrimaryGroup().getName()); } } diff --git a/test/edu/csus/ecs/pc2/core/NotificationXMLTest.java b/test/edu/csus/ecs/pc2/core/NotificationXMLTest.java index 65b99ad07..d0f6c18c6 100644 --- a/test/edu/csus/ecs/pc2/core/NotificationXMLTest.java +++ b/test/edu/csus/ecs/pc2/core/NotificationXMLTest.java @@ -24,7 +24,7 @@ /** * Test for Notifications XML. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -35,13 +35,14 @@ public class NotificationXMLTest extends AbstractTestCase { private final boolean debugMode = false; private IInternalContest contest = null; - + private SampleContest sample = null; private ClientId scoreboardClient; - + private ClientId judgeId = null; + @Override protected void setUp() throws Exception { super.setUp(); @@ -76,7 +77,7 @@ protected void setUp() throws Exception { * Add Run Judgements. */ judgeId = contest.getAccounts(Type.JUDGE).firstElement().getClientId(); - + Judgement judgement; String sampleFileName = sample.getSampleFile(); @@ -86,7 +87,7 @@ protected void setUp() throws Exception { contest.acceptRun(run, runFiles); run.setElapsedMins((run.getNumber() - 1) * 9); - + judgement = sample.getRandomJudgement(contest, run.getNumber() % 2 == 0); // ever other run is judged Yes. sample.addJudgement(contest, run, judgement, judgeId); } @@ -94,7 +95,7 @@ protected void setUp() throws Exception { /** * Assign group to team startIdx to endIdx. - * + * * @param group * @param startIdx * @param endIdx @@ -102,18 +103,19 @@ protected void setUp() throws Exception { private void assignTeamGroup(Group group, int startIdx, int endIdx) { Account[] teams = getTeamAccounts(); for (int i = startIdx; i < endIdx; i++) { - teams[i].setGroupId(group.getElementId()); + teams[i].clearGroups(); + teams[i].addGroupId(group.getElementId(), true); } } /** * Return list of accounts sorted by team id. - * + * * @return */ private Account[] getTeamAccounts() { Vector teams = contest.getAccounts(Type.TEAM); - Account[] accounts = (Account[]) teams.toArray(new Account[teams.size()]); + Account[] accounts = teams.toArray(new Account[teams.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; } @@ -123,13 +125,13 @@ public String[] getColors() { "Teal", "Violet", "White", "Yellow" }; return listOColors; } - + private Run getRunByIndex(int index) { Run[] runs = contest.getRuns(); Arrays.sort(runs, new RunComparator()); return runs[index]; } - + public void testNotification() throws Exception { NotificationXML notificationXML = new NotificationXML(); @@ -176,7 +178,7 @@ public void testNotification() throws Exception { } catch (Exception e) { pass(); } - + // TODO CCS add code to handle judgements and notifications // Judgement judgement = sample.getYesJudgement(contest); @@ -187,7 +189,7 @@ public void testNotification() throws Exception { */ sample.addBalloonNotification(contest, run); - + // TODO CCS add code to handle judgements and notifications // Notification notification = contest.getNotification(run.getSubmitter(), run.getProblemId()); // assertNotNull("Expected notification for "+run, notification); @@ -198,15 +200,15 @@ public void testNotification() throws Exception { // } } - + /** * a pass. - * + * * Passes a test, the opposite of fail(). - * + * */ private void pass() { - + } diff --git a/test/edu/csus/ecs/pc2/core/PacketHandlerTest.java b/test/edu/csus/ecs/pc2/core/PacketHandlerTest.java index 20d7604b1..4575d9724 100644 --- a/test/edu/csus/ecs/pc2/core/PacketHandlerTest.java +++ b/test/edu/csus/ecs/pc2/core/PacketHandlerTest.java @@ -1,4 +1,4 @@ -// 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.core; import java.io.IOException; @@ -25,24 +25,24 @@ /** * JUnit test for PacketHandler. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ // $HeadURL$ public class PacketHandlerTest extends AbstractTestCase { - + private SampleContest sampleContest = new SampleContest(); - + // private String outputTestDirectory; - + /** * Test security setting in PacketHandler. * */ public void testSecuritySet() throws Exception { - + IInternalContest contest = createContest("testSecuritySet"); IInternalController controller = createController(contest); @@ -53,7 +53,7 @@ public void testSecuritySet() throws Exception { ClientId serverId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Run run = contest.getRuns()[1]; - + Profile profile = new Profile("testSecuritySet Profile"); contest.setProfile(profile); @@ -110,19 +110,19 @@ public void testSecuritySet() throws Exception { //TODO: investigate how to fix this -- perhaps by using the new "proxy-team" property? // public void testDeleteRunWhenContestOver() throws Exception { public void TODOtestDeleteRunWhenContestOver() throws Exception { - + IInternalContest contest = createContest("testSecuritySet"); IInternalController controller = createController(contest); ContestTime contestTime = contest.getContestTime(); - + contest.startContest(contest.getSiteNumber()); ClientId teamId = contest.getAccounts(Type.TEAM).firstElement().getClientId(); - + ConnectionHandlerID connectionHandlerID = new ConnectionHandlerID("Client " + teamId.toString()); contest.addLogin(teamId, connectionHandlerID); - + Profile profile = new Profile("testDeleteRunWhenContestOver Profile"); contest.setProfile(profile); @@ -178,9 +178,9 @@ public void TODOtestDeleteRunWhenContestOver() throws Exception { newRun = contest.getRun(run.getElementId()); assertTrue("Run should be deleted", newRun.isDeleted()); - + } - + /** * Set CCS test mode - ON. * @param contest @@ -193,8 +193,8 @@ private void setCcsTestMode(IInternalContest contest, boolean b) { private void packetHandleRun(Run run, IInternalContest contest, IInternalController controller, ClientId teamId, ConnectionHandlerID connectionHandlerID) throws Exception { packetHandleRun(run, contest, controller, teamId, connectionHandlerID, 0); } - - private void packetHandleRun(Run run, IInternalContest contest, IInternalController controller, ClientId teamId, + + private void packetHandleRun(Run run, IInternalContest contest, IInternalController controller, ClientId teamId, ConnectionHandlerID connectionHandlerID, long overrideElapsedTime) throws Exception { RunFiles runFiles = sampleContest.createSampleRunFiles(run); @@ -207,79 +207,81 @@ private void packetHandleRun(Run run, IInternalContest contest, IInternalControl } protected IInternalContest createContest (String methodName) throws IOException, ClassNotFoundException, FileSecurityException { - + IInternalContest contest = sampleContest.createContest(2, 4, 12, 6, true); - + FileStorage storage = new FileStorage(getOutputTestFilename(methodName)); contest.setStorage(storage); - + String testSourceFileName = getSamplesSourceFilename("Sumit.java"); assertFileExists(testSourceFileName); // Add 22 random runs Run[] runs = sampleContest.createRandomRuns(contest, 22, true, true, true); sampleContest.addRuns(contest, runs, testSourceFileName); - + return contest; } - + protected IInternalController createController(IInternalContest contest) { String outputTestDirectory = getOutputDataDirectory(); return sampleContest.createController(contest, outputTestDirectory, true, false); } - - - - + + + + public void testgetProblemsForTeam() throws Exception { IInternalContest contest = createContest("testFilterProblemsByTeam"); IInternalController controller = createController(contest); - + sampleContest.assignSampleGroups(contest,"One", "Two"); - + Group[] groups = contest.getGroups(); assertNotNull(groups); assertTrue("More than 1 group "+groups.length, groups.length > 1); - + Group group3 = new Group("Three"); contest.addGroup(group3); - - + + Problem[] problems = contest.getProblems(); assertTrue(problems.length > 4); PacketHandler packetHandler = new PacketHandler(controller, contest); - + Account[] teams = getTeamAccounts(contest); - + Account team = teams[0]; - + Problem[] prob = packetHandler.getProblemsForTeam(contest, team.getClientId()); - + assertEquals(prob.length, problems.length); - - team.setGroupId(group3.getElementId()); + + team.clearGroups(); + team.addGroupId(group3.getElementId(), true); contest.updateAccount(team); - + Problem np1 = assignOnlyGroupToProblem(contest, problems[1], groups[1]); assertFalse(np1.isAllView()); List np1Groups = np1.getGroups(); assertTrue(np1Groups.size() == 1); assertEquals(groups[1], np1Groups.get(0)); - + Account admin = getAdministratorAccounts(contest)[0]; Problem[] npl = packetHandler.getProblemsForTeam(contest, admin.getClientId()); assertEquals(problems.length, npl.length); - + npl = packetHandler.getProblemsForTeam(contest, team.getClientId()); assertEquals(problems.length-1, npl.length); - + Account team2 = teams[1]; - team2.setGroupId(groups[1].getElementId()); + team2.clearGroups(); + team2.addGroupId(groups[1].getElementId(), true); contest.updateAccount(team2); - + npl = packetHandler.getProblemsForTeam(contest, team2.getClientId()); assertEquals(problems.length, npl.length); } @@ -287,11 +289,11 @@ public void testgetProblemsForTeam() throws Exception { /** * Assign only this single group for this problem. - * + * * @param contest * @param problem * @param group - * @return + * @return */ private Problem assignOnlyGroupToProblem(IInternalContest contest, Problem problem, Group group) { problem.clearGroups(); diff --git a/test/edu/csus/ecs/pc2/core/StringUtilitiesTest.java b/test/edu/csus/ecs/pc2/core/StringUtilitiesTest.java index b1aae2e2c..3f859a11e 100644 --- a/test/edu/csus/ecs/pc2/core/StringUtilitiesTest.java +++ b/test/edu/csus/ecs/pc2/core/StringUtilitiesTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 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.core; @@ -24,7 +24,7 @@ /** * Unit test. - * + * * @author pc2@ecs.csus.edu */ public class StringUtilitiesTest extends AbstractTestCase { @@ -56,7 +56,7 @@ private void compareArrayParts(String[] source, int count, String[] actual) { assertEquals(source[i], actual[i]); } } - + public void testTrunc() throws Exception { String[] data = { // "a;5;a", // @@ -67,7 +67,7 @@ public void testTrunc() throws Exception { }; for (String line : data) { - + String[] f = line.split(";"); String source = f[0]; @@ -77,10 +77,10 @@ public void testTrunc() throws Exception { String actual = StringUtilities.trunc(source, maxlen); assertEquals("trunc method ", expected, actual); } - - + + } - + /** * Test getNumberList * @throws Exception @@ -88,19 +88,19 @@ public void testTrunc() throws Exception { public void testgetNumberList() throws Exception { String [] data = { // - "1;[1]", // + "1;[1]", // "1,2,3,6-12;[1, 2, 3, 6, 7, 8, 9, 10, 11, 12]", // "5-5;[5]", // " 5 - 5 ;[5]", // "1,3,20-26;[1, 3, 20, 21, 22, 23, 24, 25, 26]", // "1, 2 ,3;[1, 2, 3]", // - "4-18;[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]", // + "4-18;[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]", // "4-12,1,5,6;[4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 5, 6]", // "100-102,123, 321 ;[100, 101, 102, 123, 321]", // "1,4,12-15,22;[1, 4, 12, 13, 14, 15, 22]", // - + }; - + for (String string : data) { String [] values =string.split(";"); String numberString =values[0]; @@ -111,10 +111,10 @@ public void testgetNumberList() throws Exception { assertEquals("Expected range for "+numberString, expected, Arrays.toString(out)); } } - + /** * Test getNumberList for invalid input number strings. - * + * * @throws Exception */ public void testgetNumberListNegative() throws Exception { @@ -144,10 +144,10 @@ public void testgetNumberListNegative() throws Exception { } } - + protected Run [] add90Runs(IInternalContest contest) throws Exception { - + String [] runsDataList = { // "1,16,B,1,No", // "2,8,C,1,No", // @@ -248,9 +248,9 @@ public void testgetNumberListNegative() throws Exception { return contest.getRuns(); } - + protected Run [] addTc1Runs(IInternalContest contest) throws Exception { - + String [] runsDataList = { // "1,101,B,1,Yes", // "2,151,C,1,Yes", // @@ -283,17 +283,17 @@ public void testgetNumberListNegative() throws Exception { } - + private void initializeStaticLog(String name) { StaticLog.setLog(new Log("logs", name + ".log")); } public void testgetRunsForUser() throws Exception { - + initializeStaticLog(getName()); InternalContest contest = new InternalContest(); String cdpDir = "C:/repos/PacNWSpring2023/testcontest1/config"; - + if (! new File(cdpDir).isDirectory()) { // TODO coipy testcontest1 into testdata or samples then use that path System.out.println("testgetRunsForUser Irnoring test using "+cdpDir); @@ -303,12 +303,12 @@ public void testgetRunsForUser() throws Exception { loader.initializeContest(contest, new File( cdpDir)); Group[] groups = contest.getGroups(); - + for (Group group : groups) { String divName = ScoreboardUtilities.getDivision(group.getDisplayName()); assertNotNull("No division found for "+group.getDisplayName(), divName); } - + Account[] accounts = getTeamAccounts(contest); Arrays.sort(accounts, new AccountComparator()); @@ -317,16 +317,16 @@ public void testgetRunsForUser() throws Exception { assertEquals("Expecting # jugements", 10, judgements.length); addTc1Runs(contest); - + Run[] runlist = contest.getRuns(); for (Run run : runlist) { assertNotNull("Expecting account for "+run.getSubmitter(), contest.getAccount(run.getSubmitter())); String div = ScoreboardUtilities.getDivision(contest, run.getSubmitter()); assertNotNull("Missing division for "+run.getSubmitter(), div); } - + ClientId client1 = accounts[5].getClientId(); - Group group = contest.getGroup(contest.getAccount(client1).getGroupId()); + Group group = contest.getGroup(contest.getAccount(client1).getPrimaryGroupId()); Run[] runs = ScoreboardUtilities.getRunsForUserDivision(client1, contest); assertEquals("Expecting runs matching group " + group, 7, runs.length); @@ -335,19 +335,19 @@ public void testgetRunsForUser() throws Exception { runs = ScoreboardUtilities.getRunsForUserDivision(client1, contest); assertEquals("Expecting runs matching group " + group, 5, runs.length); - accounts[12].setGroupId(null); // test for null group + accounts[12].clearGroups(); // test for null group contest.updateAccounts(accounts); runs = ScoreboardUtilities.getRunsForUserDivision(client1, contest); assertEquals("Expecting runs matching group " + group, 0, runs.length); - + } - + /** * Load 9 judgements, including AC into contest * @param contest */ public void loadJudgement(IInternalContest contest) { - + String[] judgements = {"Stupid programming error", "Misread problem statement", "Almost there", "You have no clue", "Give up and go home", "Consider switching to another major", "How did you get into this place ?", "Contact Staff - you have no hope" }; String[] acronymns = {"CE", "WA", "TLE", "WA2", "RTE", "OFE", "WA3", "JE" }; @@ -363,14 +363,16 @@ public void loadJudgement(IInternalContest contest) { } } - + // TODO REFACTOR move to AbstractTestcase + @Override public String getSampleContestsDirectory() { return "samps" + File.separator + "contests"; } - - + + // TODO REFACTOR move to AbstractTestcase + @Override public String getTestSampleContestDirectory(String dirname) { return getSampleContestsDirectory() + File.separator + dirname; } @@ -405,13 +407,13 @@ public void testgetDivision() throws Exception { } } - + public void testgetTeamNumber() throws Exception { - + // 016.991|INFO|qtp49752459-26|log|Standings requested by team team101 - + String user = "team101".toLowerCase(); Integer num = StringUtilities.getTeamNumber(user); assertEquals("Expecting number for "+user, 101, num.intValue()); @@ -424,7 +426,7 @@ public void testgetTeamNumber() throws Exception { num = StringUtilities.getTeamNumber(user); System.out.println("debug 22 num num "+num); assertNull("Expecting null for "+user, num); - + } diff --git a/test/edu/csus/ecs/pc2/core/imports/LoadAccountsTest.java b/test/edu/csus/ecs/pc2/core/imports/LoadAccountsTest.java index 7468eb389..d1e64d063 100644 --- a/test/edu/csus/ecs/pc2/core/imports/LoadAccountsTest.java +++ b/test/edu/csus/ecs/pc2/core/imports/LoadAccountsTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 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.core.imports; import java.io.File; @@ -20,7 +20,7 @@ /** * Unit test. - * + * * @author Troy */ public class LoadAccountsTest extends AbstractTestCase { @@ -28,7 +28,7 @@ public class LoadAccountsTest extends AbstractTestCase { private Site[] sites = new Site[2]; private AccountList accountList = new AccountList(); - + private SampleContest sample = new SampleContest(); public LoadAccountsTest() { @@ -39,8 +39,9 @@ public LoadAccountsTest(String arg0) { super(arg0); } + @Override protected void setUp() throws Exception { - + sites[0] = new Site("SOUTH", 2); sites[1] = new Site("NORTH", 1); accountList.generateNewAccounts(ClientType.Type.TEAM, 45, PasswordType.JOE, 1, true); @@ -49,15 +50,16 @@ protected void setUp() throws Exception { } public void testOne() throws Exception { - + String testDataDir = getRootInputTestDataDirectory(); String accountsFilename = testDataDir + File.separator + "loadaccount" + File.separator + "accounts.txt"; assertFileExists(accountsFilename); - + LoadAccounts loadAccounts = new LoadAccounts(); Account account = accountList.getAccount(new ClientId(1, ClientType.Type.TEAM, 1)); Group group = new Group("Group 1"); - account.setGroupId(group.getElementId()); + account.clearGroups(); + account.addGroupId(group.getElementId(), true); // these were broken in 1052 account.setLongSchoolName("California State University, Sacramento"); account.setShortSchoolName("CSUS"); @@ -73,15 +75,16 @@ public void testOne() throws Exception { } public void testTwo() throws Exception { - + String testDataDir = getRootInputTestDataDirectory(); String accountsFilename = testDataDir + File.separator + File.separator + "loadaccount" + File.separator + "accounts.min.txt"; assertFileExists(accountsFilename); - + LoadAccounts loadAccounts = new LoadAccounts(); Account account = accountList.getAccount(new ClientId(1, ClientType.Type.TEAM, 1)); Group group = new Group("Group 1"); - account.setGroupId(group.getElementId()); + account.clearGroups(); + account.addGroupId(group.getElementId(), true); // these were broken in 1052 account.setLongSchoolName("California State University, Sacramento"); account.setShortSchoolName("CSUS"); @@ -98,12 +101,12 @@ public void testTwo() throws Exception { } public void testThree() throws Exception { - + String testDataDir = getRootInputTestDataDirectory(); String accountsFilename = testDataDir + File.separator + File.separator + "loadaccount" + File.separator + "accounts.perm1.txt"; assertFileExists(accountsFilename); - - + + LoadAccounts loadAccounts = new LoadAccounts(); Account teamAccount = accountList.getAccount(new ClientId(1, ClientType.Type.TEAM, 1)); accountList.update(teamAccount); @@ -117,34 +120,34 @@ public void testThree() throws Exception { checkPermissions(accounts); } public void testFour() throws Exception { - + String testDataDir = getRootInputTestDataDirectory(); String accountsFilename = testDataDir + File.separator + "loadaccount" + File.separator + "accounts.649.txt"; assertFileExists(accountsFilename); - LoadAccounts loadAccounts = new LoadAccounts(); - Account teamAccount = accountList.getAccount(new ClientId(1, ClientType.Type.TEAM, 1)); - accountList.update(teamAccount); - - - // 649 is checking group column vs externalid column - Group[] groups = new Group[1]; - groups[0] = new Group("Lower"); - Account[] accounts = loadAccounts.fromTSVFile(accountsFilename, accountList.getList(), groups); - for (int i = 0; i < accounts.length; i++) { - if (accounts[i].getClientId().equals(teamAccount.getClientId())) { - assertEquals("group load",groups[0].getElementId(),accounts[i].getGroupId()); - assertEquals("externalId load","10", accounts[i].getExternalId()); - break; - } + LoadAccounts loadAccounts = new LoadAccounts(); + Account teamAccount = accountList.getAccount(new ClientId(1, ClientType.Type.TEAM, 1)); + accountList.update(teamAccount); + + + // 649 is checking group column vs externalid column + Group[] groups = new Group[1]; + groups[0] = new Group("Lower"); + Account[] accounts = loadAccounts.fromTSVFile(accountsFilename, accountList.getList(), groups); + for (int i = 0; i < accounts.length; i++) { + if (accounts[i].getClientId().equals(teamAccount.getClientId())) { + assertTrue("group load",accounts[i].isGroupMember(groups[0].getElementId())); + assertEquals("externalId load","10", accounts[i].getExternalId()); + break; } + } } public void testScoreAdjustment() throws Exception { - + String testDataDir = getRootInputTestDataDirectory(); String accountsFilename = testDataDir + File.separator + "loadaccount" + File.separator + "accounts.scoreadjustment.txt"; assertFileExists(accountsFilename); - + LoadAccounts loadAccounts = new LoadAccounts(); Account teamAccount = accountList.getAccount(new ClientId(1, ClientType.Type.TEAM, 1)); accountList.update(teamAccount); @@ -162,7 +165,7 @@ public void testScoreAdjustment() throws Exception { } } } - + public void checkPermissions(Account[] accounts) { for (Account account : accounts) { if (account.getClientId().getClientType().equals(ClientType.Type.TEAM)) { @@ -175,28 +178,28 @@ public void checkPermissions(Account[] accounts) { } } - + protected void generateFile(IInternalContest contest, Formats format, String outputFile) throws Exception { - + Group[] groups = contest.getGroups(); Account[] accounts = SampleContest.getTeamAccounts(contest); - + assertEquals("Team accounts ", 120, accounts.length); assertEquals("Groups ", 2, groups.length); - + ExportAccounts.saveAccounts(format, accounts, groups, new File(outputFile)); - + if (ExportAccounts.getException() != null){ throw ExportAccounts.getException(); } - + } - + /** * Test load for 3 new institution fields. - * + * * Bug 1067 test. - * + * * @throws Exception */ public void testLoadTXTFile() throws Exception { @@ -234,12 +237,12 @@ public void testLoadTXTFile() throws Exception { } private void testAccountFields( IInternalContest contest, int teamNumber, String longInst, String shortInst, String countryCode) { - + Account[] accounts = SampleContest.getTeamAccounts(contest, 3); Arrays.sort(accounts, new AccountComparator()); - + Account team = accounts[teamNumber - 1]; - + testAccountFields(team, longInst, shortInst, countryCode); // dumpTeam(team); @@ -247,7 +250,7 @@ private void testAccountFields( IInternalContest contest, int teamNumber, String // assertEquals("int long name, " +teamId, longInst, team.getLongSchoolName()); // assertEquals("int short name, team " + teamId, shortInst, team.getShortSchoolName()); // assertEquals("country code, team " + teamId, countryCode, team.getCountryCode()); - + } private void testAccountFields(Account team, String longInst, String shortInst, String countryCode) { @@ -271,5 +274,5 @@ protected void dumpTeam(Account team) { } - + } diff --git a/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java b/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java index ca0a173e4..f712813cb 100644 --- a/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java +++ b/test/edu/csus/ecs/pc2/core/imports/LoadICPCTSVDataTest.java @@ -19,7 +19,7 @@ /** * Unit Tests for LoadICPCTSVData. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -27,7 +27,7 @@ // $HeadURL$ public class LoadICPCTSVDataTest extends AbstractTestCase { - + public void testCheckFiles() throws Exception { LoadICPCTSVData load = new LoadICPCTSVData(); @@ -40,10 +40,10 @@ public void testCheckFiles() throws Exception { Account[] accounts = ICPCTSVLoader.loadAccounts(load.getTeamsFilename()); assertEquals("Number of groups", 6, groups.length); - assertEquals("Number of acounts", 3, accounts.length); + assertEquals("Number of accounts", 3, accounts.length); } - + public void testLoad() throws Exception { LoadICPCTSVData load = new LoadICPCTSVData(); @@ -56,7 +56,7 @@ public void testLoad() throws Exception { Account[] accounts = ICPCTSVLoader.loadAccounts(load.getTeamsFilename()); assertEquals("Number of groups", 6, groups.length); - assertEquals("Number of acounts", 3, accounts.length); + assertEquals("Number of accounts", 3, accounts.length); IInternalContest contest = new SampleContest().createContest(1, 1, 12, 12, true); @@ -64,11 +64,11 @@ public void testLoad() throws Exception { List groupList = Arrays.asList(groups); List accountList = Arrays.asList(accounts); - checkIfGroupsAssigned(accountList); - + checkIfGroupsAssigned(contest, accountList); + load.updateGroupsAndAccounts(contest, groupList, accountList); - checkIfGroupsAssigned(accountList); + checkIfGroupsAssigned(contest, accountList); checkIfTeamAssignments(accountList); @@ -78,17 +78,65 @@ public void testLoad() throws Exception { assertEquals("Display Name", "University of Chile", chile.getDisplayName()); assertEquals("Team Name", "Natural Log", chile.getTeamName()); assertEquals("Country Code", "CL", chile.getCountryCode()); - + edu.csus.ecs.pc2.core.security.Permission.Type[] permList = getPermList(Type.TEAM); - + assertEquals("Expecting same permissions ", 7, permList.length); - ElementId id = chile.getGroupId(); + ElementId id = chile.getPrimaryGroupId(); Group groupId = lookupGroup(groupList, id); assertEquals("Group Id ", 206, groupId.getGroupId()); } + public void testLoadMultipleGroups() throws Exception { + LoadICPCTSVData load = new LoadICPCTSVData(); + + String groupsFilename = getTestFilename(LoadICPCTSVData.GROUPS_FILENAME); + + assertFileExists(groupsFilename); + load.checkFiles(groupsFilename); + + Group[] groups = ICPCTSVLoader.loadGroups(load.getGroupsFilename()); + Account[] accounts = ICPCTSVLoader.loadAccounts(load.getTeamsFilename()); + + assertEquals("Number of groups", 6, groups.length); + assertEquals("Number of accounts", 3, accounts.length); + + IInternalContest contest = new SampleContest().createContest(1, 1, 12, 12, true); + + assertNotNull("contest", contest); + + List groupList = Arrays.asList(groups); + List accountList = Arrays.asList(accounts); + checkIfGroupsAssigned(contest, accountList); + + load.updateGroupsAndAccounts(contest, groupList, accountList); + + checkIfGroupsAssigned(contest, accountList); + + checkIfTeamAssignments(accountList); + + Account co = accountList.get(1); + + assertEquals("External name ", "University of Colorado", co.getExternalName()); + assertEquals("Display Name", "University of Colorado", co.getDisplayName()); + assertEquals("Team Name", "Rams", co.getTeamName()); + assertEquals("Country Code", "USA", co.getCountryCode()); + + edu.csus.ecs.pc2.core.security.Permission.Type[] permList = getPermList(Type.TEAM); + + assertEquals("Expecting same permissions ", 7, permList.length); + + // We know at least one group is assigned, or we'd assert above (checkIfGroupsAssigned) + for(ElementId id : co.getGroupIds()) { + Group groupId = lookupGroup(groupList, id); + assertNotNull("Group Id " + id.toString(), groupId); + int gid = groupId.getGroupId(); + assertTrue("Wrong group id", gid == 202 || gid == 204 || gid == 503); + } + } + private Group lookupGroup(List groupList, ElementId id) { for (Group group : groupList) { if (group.getElementId().equals(id)) { @@ -107,15 +155,24 @@ private void checkIfTeamAssignments(List accountList) { } - private void checkIfGroupsAssigned(List accountList) { + private void checkIfGroupsAssigned(IInternalContest contest, List accountList) { + int num = 1; for (Account account : accountList) { - if (account.getGroupId() == null) { + if (account.getGroupIds() == null) { fail("Expecting group assigned to account " + account); } + int n = account.getGroupIds().size(); + if(num == 2) { + // account index 2 has 3 groups assigned, the "A" groups (any group starting with A). + assertEquals("Expecting 3 groups for account", 3, n); + } else if(n != 1) { + assertEquals("Expecting 1 group for account", 1, n); + } + num++; } } - + private IInternalContest loadSampleContest(IInternalContest contest, String sampleName) throws Exception { IContestLoader loader = new ContestSnakeYAMLLoader(); String configDir = getTestSampleContestConfigDirectory(sampleName); @@ -138,10 +195,10 @@ private String getTestSampleContestConfigFile(String contestName, String filenam /** * Test loading a teams.tsv twice. - * + * * The 2nd load of teams and groups, in particular groups, caused all accounts' groups to be "empty" * on the Accounts tab. See https://github.com/pc2ccs/pc2v9/issues/318 for details. - * + * * @throws Exception */ public void testReLoadTSV() throws Exception { @@ -153,10 +210,10 @@ public void testReLoadTSV() throws Exception { IInternalContest contest = loadSampleContest(null, contestName); assertNotNull(contest); IInternalController controller = new SampleContest().createController(contest, true, false); - + LoadICPCTSVData loader = new LoadICPCTSVData(); loader.setContestAndController(contest, controller); - + boolean loaded = loader.loadFiles(groupsFilename, false, false); assertTrue("Expecting "+contestName+" contest loaded", loaded); @@ -189,8 +246,8 @@ public void testReLoadTSV() throws Exception { // check that all accounts assigned groups in the model/contest for (Account account : accounts2) { - Group group = contest.getGroup(account.getGroupId()); - assertNotNull("Expecting group to exist in contest/model " + account.getGroupId(), group); + Group group = contest.getGroup(account.getPrimaryGroupId()); + assertNotNull("Expecting group to exist in contest/model " + account.getPrimaryGroupId(), group); } } } diff --git a/test/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilitiesTest.java b/test/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilitiesTest.java index bb36856be..dd4605973 100644 --- a/test/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilitiesTest.java +++ b/test/edu/csus/ecs/pc2/core/imports/clics/CLICSAwardUtilitiesTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 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.core.imports.clics; import java.io.File; @@ -18,23 +18,23 @@ import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientId; import edu.csus.ecs.pc2.core.model.ClientType; +import edu.csus.ecs.pc2.core.model.ClientType.Type; import edu.csus.ecs.pc2.core.model.FinalizeData; import edu.csus.ecs.pc2.core.model.Group; -import edu.csus.ecs.pc2.core.model.ClientType.Type; -import edu.csus.ecs.pc2.core.standings.ContestStandings; -import edu.csus.ecs.pc2.core.standings.ScoreboardUtilities; -import edu.csus.ecs.pc2.core.standings.TeamStanding; import edu.csus.ecs.pc2.core.model.IInternalContest; import edu.csus.ecs.pc2.core.model.InternalContest; import edu.csus.ecs.pc2.core.model.Judgement; import edu.csus.ecs.pc2.core.model.Language; import edu.csus.ecs.pc2.core.model.Problem; import edu.csus.ecs.pc2.core.model.SampleContest; +import edu.csus.ecs.pc2.core.standings.ContestStandings; +import edu.csus.ecs.pc2.core.standings.ScoreboardUtilities; +import edu.csus.ecs.pc2.core.standings.TeamStanding; import edu.csus.ecs.pc2.core.util.AbstractTestCase; /** * Unit tests. - * + * * @author Douglas A. Lane */ public class CLICSAwardUtilitiesTest extends AbstractTestCase { @@ -43,7 +43,7 @@ public class CLICSAwardUtilitiesTest extends AbstractTestCase { /** * Test no awards (no runs) in awards.json - * + * * @throws Exception */ public void testWithNoRuns() throws Exception { @@ -63,10 +63,10 @@ public void testWithNoRuns() throws Exception { assertEquals("Expecting no award rows ", 0, rowsWritten); } - + /** * Dump standings information. - * + * * @param message * @param contest * @throws JsonParseException @@ -76,21 +76,31 @@ public void testWithNoRuns() throws Exception { * @throws IOException */ public void dumpStandings(String message, IInternalContest contest) throws JsonParseException, JsonMappingException, JAXBException, IllegalContestState, IOException { - + System.out.println("dumpStandings: "+message); ContestStandings contestStandings = ScoreboardUtilities.createContestStandings(contest); List teamStands = contestStandings.getTeamStandings(); for (TeamStanding teamStanding : teamStands) { ClientId clientId = CLICSAwardUtilities.createClientId(teamStanding); - Group teamGroup = CLICSAwardUtilities.getGroupForTeam(contest, clientId); - if (teamGroup == null) { + List teamGroups = CLICSAwardUtilities.getGroupsForTeam(contest, clientId); + if (teamGroups == null) { System.out.println(teamStanding.getRank()+" "+teamStanding.getSolved()+" "+teamStanding.getPoints() + " " + // - teamStanding.getTeamName() + " school:" + teamStanding.getShortSchoolName() + " TEAM HAS NO GROUP"); + teamStanding.getTeamName() + " school:" + teamStanding.getShortSchoolName() + " TEAM HAS NO GROUPS"); } else { + String allgroups = ""; + boolean firstGroup = true; + for(Group group : teamGroups) { + if(!firstGroup) { + allgroups = allgroups + ","; + } else { + firstGroup = false; + } + allgroups = allgroups + group.getGroupId() + ":" + group.getDisplayName(); + } System.out.println(teamStanding.getRank()+" "+teamStanding.getSolved()+" "+teamStanding.getPoints() + " " + // teamStanding.getTeamName() + " school=" + teamStanding.getShortSchoolName() + " " + // - teamGroup.getGroupId()+":"+teamGroup.getDisplayName()); + allgroups); } } } @@ -99,7 +109,7 @@ public void testcreateAwardsListFor5awards() throws Exception { /** * runsData columns. - * + * * 0 - run id, int * 1 - team id, int * 2 - problem letter, char @@ -109,31 +119,31 @@ public void testcreateAwardsListFor5awards() throws Exception { * 6 - No Judgement index */ String[] runsData = { // - "1,1,A,1,No,No,4",// + "1,1,A,1,No,No,4",// "2,1,A,1,No,No,2", // - "3,1,A,1,No,No,1", // - "4,1,A,3,Yes,No,0", // - "5,1,A,5,No,No,1", - "6,1,A,7,Yes,No,0", - "7,1,A,9,No,No,1", - "8,1,B,11,No,No,1", - "9,2,A,48,No,No,4", - "10,2,A,50,Yes,No,0", - "11,2,C,35,Yes,No,0", - "12,2,D,40,Yes,No,0", + "3,1,A,1,No,No,1", // + "4,1,A,3,Yes,No,0", // + "5,1,A,5,No,No,1", + "6,1,A,7,Yes,No,0", + "7,1,A,9,No,No,1", + "8,1,B,11,No,No,1", + "9,2,A,48,No,No,4", + "10,2,A,50,Yes,No,0", + "11,2,C,35,Yes,No,0", + "12,2,D,40,Yes,No,0", }; InternalContest contest = createContestWithJudgedRuns(12, runsData, 8); assertNotNull(contest); - + assertEquals("Expecting groups", 2, contest.getGroups().length); List awards = CLICSAwardUtilities.createAwardsList(contest); // dumpAwards ("debug DA ",System.out, awards); // dumpStandings("debug XX ", contest); - + assertAwardCount(1, awards, CLICSAwardUtilities.WINNER_S_OF_GROUP_TITLE); assertEquals("Awards expected ", 6, awards.size()); @@ -149,10 +159,10 @@ public void testcreateAwardsListFor5awards() throws Exception { // editFile(awardsFile, "debug C "+getName()); } - + /** - * If expectedCount found in citation matching searchForString. + * If expectedCount found in citation matching searchForString. * @param expectedCount * @param awards * @param searchForString @@ -171,14 +181,14 @@ private void assertAwardCount(int expectedCount, List awards, String /** * Test with runs with only No judgement runs. No awards output. - * + * * @throws Exception */ public void testWithNoAwards() throws Exception { /** * runsData columns. - * + * * 0 - run id, int * 1 - team id, int * 2 - problem letter, char @@ -206,7 +216,7 @@ public void testWithNoAwards() throws Exception { InternalContest contest = createContestWithJudgedRuns(12, runsData, 8); assertNotNull(contest); - + assertEquals("Expecting groups", 2, contest.getGroups().length); List awards = CLICSAwardUtilities.createAwardsList(contest); @@ -216,7 +226,7 @@ public void testWithNoAwards() throws Exception { String outdir = getOutputDataDirectory(getName()); ensureDirectory(outdir); String awardsFile = outdir + File.separator + "awards.json"; - + int rowsWritten = CLICSAwardUtilities.writeAwardsJSONFile(awardsFile, awards); assertEquals("Expecting awards elements ", 0, rowsWritten); @@ -224,14 +234,14 @@ public void testWithNoAwards() throws Exception { /** * Test with two awards. - * + * * @throws Exception */ public void testWithTwoAwards() throws Exception { /** * runsData columns. - * + * * 0 - run id, int * 1 - team id, int * 2 - problem letter, char @@ -240,7 +250,7 @@ public void testWithTwoAwards() throws Exception { * 5 - send to teams, Yes or No * 6 - No Judgement index */ - + String[] runsData = { // "1,2,A,1,Yes,No,0", // "2,1,A,1,No,No,2", // @@ -250,12 +260,12 @@ public void testWithTwoAwards() throws Exception { InternalContest contest = createContestWithJudgedRuns(12, runsData, 8); assertNotNull(contest); - + assertEquals("Expecting groups", 2, contest.getGroups().length); List awards = CLICSAwardUtilities.createAwardsList(contest); - - + + assertEquals("Awards expected ", 4, awards.size()); @@ -265,15 +275,15 @@ public void testWithTwoAwards() throws Exception { int rowsWritten = CLICSAwardUtilities.writeAwardsJSONFile(awardsFile, awards); assertEquals("Expecting no award rows ", 4, rowsWritten); - + // editFile(awardsFile, "debug A "+getName()); } - + public void testAllAwards() throws Exception { - + /** * runsData columns. - * + * * 0 - run id, int * 1 - team id, int * 2 - problem letter, char @@ -282,12 +292,12 @@ public void testAllAwards() throws Exception { * 5 - send to teams, Yes or No * 6 - No Judgement index */ - + String[] runsData = { // - "1,1,A,1,No,No,4",// + "1,1,A,1,No,No,4",// "2,1,A,1,No,No,2", // - "3,1,A,1,No,No,1", // - "4,1,A,3,Yes,No,0", // + "3,1,A,1,No,No,1", // + "4,1,A,3,Yes,No,0", // "5,1,A,5,No,No,1", // "6,1,A,7,Yes,No,0", // "7,1,A,9,No,No,1", // @@ -312,15 +322,15 @@ public void testAllAwards() throws Exception { "25,17,F,266,Yes,No,0", // "26,18,F,366,Yes,No,0", // "27,19,F,866,Yes,No,0", // - + "28,12,F,1466,Yes,No,0", // "20,11,A, 123,Yes,No,0", // - + "21,22,F,1466,Yes,No,0", // "22,22,A, 123,Yes,No,0", // "28,23,F,1466,Yes,No,0", // "24,24,A, 123,Yes,No,0", // - + }; @@ -328,24 +338,24 @@ public void testAllAwards() throws Exception { InternalContest contest = createContestWithJudgedRuns(30, runsData, numProbs); assertNotNull(contest); - + assertEquals("Expecting groups", 2, contest.getGroups().length); List awards = CLICSAwardUtilities.createAwardsList(contest); - + // dumpAwards("test All ", System.out, awards); - + assertEquals("Awards expected ", 12, awards.size()); - - + + } - + public void testAwardsTwo() throws Exception { - + /** * runsData columns. - * + * * 0 - run id, int * 1 - team id, int * 2 - problem letter, char @@ -354,19 +364,19 @@ public void testAwardsTwo() throws Exception { * 5 - send to teams, Yes or No * 6 - No Judgement index */ - + String[] runsData = { // "1,1,A,1,Yes,No,0", // "2,2,A,2,Yes,No,0", // "3,3,A,3,Yes,No,0", // "4,4,A,4,Yes,No,0", // - + "5,5,A,5,Yes,No,0", // "6,6,A,6,Yes,No,0", // "7,7,A,7,Yes,No,0", // "8,8,A,8,Yes,No,0", // - - + + "9,9,A,9,Yes,No,0", // "10,10,A,13,Yes,No,0", // "11,11,A,13,Yes,No,0", // @@ -378,7 +388,7 @@ public void testAwardsTwo() throws Exception { "17,17,A,13,Yes,No,0", // "18,18,A,13,Yes,No,0", // "19,19,A,13,Yes,No,0", // - + "20,20,A,23,Yes,No,0", // "21,21,A,21,Yes,No,0", // "22,22,A,22,Yes,No,0", // @@ -394,7 +404,7 @@ public void testAwardsTwo() throws Exception { // "24,24,A,32,Yes,No,0", // }; - + int numProbs = 8; InternalContest contest = createContestWithJudgedRuns(40, runsData, numProbs); @@ -404,7 +414,7 @@ public void testAwardsTwo() throws Exception { assertEquals("Expecting groups", 2, contest.getGroups().length); List awards = CLICSAwardUtilities.createAwardsList(contest); - + // dumpStandings("debug Two", contest); assertTeamCount(awards, CLICSAwardUtilities.ID_WINNER, 1); @@ -414,14 +424,14 @@ public void testAwardsTwo() throws Exception { assertTeamCount(awards, CLICSAwardUtilities.ID_BRONZE_MEDAL, 11); // dumpAwards (System.out, awards); - + assertEquals("Awards expected ", 7, awards.size()); - - + + FinalizeData data = createFinalizeData(4, 4, 13); contest.setFinalizeData(data); awards = CLICSAwardUtilities.createAwardsList(contest); - + assertTeamCount(awards, CLICSAwardUtilities.ID_WINNER, 1); assertTeamCount(awards, CLICSAwardUtilities.ID_GOLD_MEDAL, 4); @@ -430,7 +440,7 @@ public void testAwardsTwo() throws Exception { assertEquals("Awards expected ", 7, awards.size()); } - + protected void dumpAwards(String message, PrintStream out, List awards) throws JsonProcessingException { out.println("dumpAwards: "+message); for (CLICSAward clicsAward : awards) { @@ -446,7 +456,7 @@ protected FinalizeData createFinalizeData(int numberGolds, int numberSilvers, in data.setComment("Finalized by Director of Operations, no, really!"); return data; } - + /** * Expect team count for awards id to be the same as expectedNumber * @param awards @@ -469,14 +479,14 @@ private String[] getTeamList(List awards, String id) { /** * Test with 23 runs and 8 awards. - * + * * @throws Exception */ public void testEightAwards() throws Exception { - + /** * runsData columns. - * + * * 0 - run id, int * 1 - team id, int * 2 - problem letter, char @@ -485,12 +495,12 @@ public void testEightAwards() throws Exception { * 5 - send to teams, Yes or No * 6 - No Judgement index */ - + String[] runsData = { // - "1,1,A,1,No,No,4",// + "1,1,A,1,No,No,4",// "2,1,A,1,No,No,2", // - "3,1,A,1,No,No,1", // - "4,1,A,3,Yes,No,0", // + "3,1,A,1,No,No,1", // + "4,1,A,3,Yes,No,0", // "5,1,A,5,No,No,1", // "6,1,A,7,Yes,No,0", // "7,1,A,9,No,No,1", // @@ -516,14 +526,14 @@ public void testEightAwards() throws Exception { InternalContest contest = createContestWithJudgedRuns(12, runsData, numProbs); assertNotNull(contest); - + assertEquals("Expecting groups", 2, contest.getGroups().length); List awards = CLICSAwardUtilities.createAwardsList(contest); - + List list = new ArrayList(); CLICSAwardUtilities.addMedals(contest, list); - + assertEquals("Awards expected ", 9, awards.size()); String outdir = getOutputDataDirectory(getName()); @@ -532,15 +542,15 @@ public void testEightAwards() throws Exception { int rowsWritten = CLICSAwardUtilities.writeAwardsJSONFile(awardsFile, awards); assertEquals("Expecting no award rows ", 9, rowsWritten); - + // editFile(awardsFile, "debug A "+getName()); - - + + } - + /** * Test load awards.json from file. - * + * * @throws Exception */ public void testreadAwardsList() throws Exception { @@ -603,7 +613,7 @@ public void testreadAwardsList() throws Exception { /** * Compare award for id with team number - * + * * @param awards * @param id * the name of the award @@ -625,7 +635,7 @@ private void assertCitationEquals(List awards, String id, String tea /** * Find award in awards list - * + * * @param awards * @param id the award id * @return @@ -643,7 +653,7 @@ private CLICSAward findAward(List awards, String id) throws JsonProc /** * Create and return a new scoreboard client. - * + * * @param contest * @return a ClientId for newly created scoreboard account. */ @@ -654,7 +664,7 @@ private ClientId createBoardAccount(IInternalContest contest) { /** * Insure that there is one team and one judge in the contest model. - * + * * @param contest */ private void checkForJudgeAndTeam(IInternalContest contest) { @@ -669,7 +679,7 @@ private void checkForJudgeAndTeam(IInternalContest contest) { } /** * Initialize contest with teams, problems, languages, judgements. - * + * * @param contest * @param numTeams * @param numProblems @@ -679,10 +689,10 @@ private void initData(IInternalContest contest, int numTeams, int numProblems) { // Add accounts contest.generateNewAccounts(ClientType.Type.TEAM.toString(), numTeams, true); contest.generateNewAccounts(ClientType.Type.JUDGE.toString(), 6, true); - + sampleContest = new SampleContest(); sampleContest.assignSampleGroups(contest, "Group Thing One", "Group Thing Two"); - + // Add scoreboard account and set the scoreboard account for this client (in contest) contest.setClientId(createBoardAccount(contest)); @@ -714,7 +724,7 @@ private void initData(IInternalContest contest, int numTeams, int numProblems) { /** * Create contest with judged runs. - * + * * @param numTeams * number of teams to create * @param runsDataList @@ -728,7 +738,7 @@ public InternalContest createContestWithJudgedRuns(int numTeams, String[] runsDa /** * Create contest with judged runs. - * + * * @param numTeams * number of teams to create * @param runsDataList diff --git a/test/edu/csus/ecs/pc2/core/list/AccountListTest.java b/test/edu/csus/ecs/pc2/core/list/AccountListTest.java index be8e4e8af..326c6cbfa 100644 --- a/test/edu/csus/ecs/pc2/core/list/AccountListTest.java +++ b/test/edu/csus/ecs/pc2/core/list/AccountListTest.java @@ -1,19 +1,19 @@ -// 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.core.list; import java.io.PrintStream; import java.util.Arrays; import java.util.Vector; -import junit.framework.TestCase; import edu.csus.ecs.pc2.core.list.AccountList.PasswordType; import edu.csus.ecs.pc2.core.model.Account; import edu.csus.ecs.pc2.core.model.ClientType.Type; import edu.csus.ecs.pc2.core.security.Permission; +import junit.framework.TestCase; /** * Test AccountList. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -23,10 +23,12 @@ public class AccountListTest extends TestCase { private boolean debugMode = false; + @Override protected void setUp() throws Exception { super.setUp(); } + @Override protected void tearDown() throws Exception { super.tearDown(); } @@ -57,73 +59,73 @@ public void testCreate() throws Exception { assertTrue("Should be no Judge accounts site 1", judgesAt1.size() == 0); } - + Account[] getOrderedAccounts(AccountList accountList, Type type, int siteNumber) { Vector vector = accountList.getAccounts(type, siteNumber); - Account[] accounts = (Account[]) vector.toArray(new Account[vector.size()]); + Account[] accounts = vector.toArray(new Account[vector.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; } - + public void testReg() throws Exception { - + AccountList accountList = new AccountList(); int siteNumber = 3; - + String teamName = "Foo Fighters"; String[] memberNames = { "C", "BB", "AAA" }; String pass = "letMeGo"; Account newRegisterAccount = accountList.assignNewTeam(siteNumber, teamName, memberNames, pass); - + assertEquals(newRegisterAccount, teamName, memberNames, pass); - + Account[] list = getOrderedAccounts(accountList, Type.TEAM, siteNumber); - + Account account = list[0]; assertEquals(account, teamName, memberNames, pass); - - + + } - + public void testRegWithExistingAccounts() throws Exception { - + AccountList accountList = new AccountList(); int siteNumber = 3; - + accountList.generateNewAccounts(Type.TEAM, 12, PasswordType.JOE, siteNumber, true); assignAccountNames (accountList, siteNumber); - + String teamName = "Foo Fighters"; String[] memberNames = { "C", "BB", "AAA" }; String pass = "letMeGo"; - + Account newRegisterAccount = accountList.assignNewTeam(siteNumber, teamName, memberNames, pass); - + assertEquals(newRegisterAccount, teamName, memberNames, pass); - + Account[] list = getOrderedAccounts(accountList, Type.TEAM, siteNumber); - + int len = list.length-1; Account account = list[len]; assertEquals(account, teamName, memberNames, pass); - + if (debugMode) { printAccount(System.out, account); } - + accountList.generateNewAccounts(Type.TEAM, 12, PasswordType.JOE, siteNumber, true); - + teamName = "Foo Fighters 2"; newRegisterAccount = accountList.assignNewTeam(siteNumber, teamName, memberNames, pass); list = getOrderedAccounts(accountList, Type.TEAM, siteNumber); - + account = list[len + 1]; assertEquals(account, teamName, memberNames, pass); - + } private void assignAccountNames(AccountList accountList, int siteNumber) { @@ -135,7 +137,7 @@ private void assignAccountNames(AccountList accountList, int siteNumber) { /** * Compares some of account fields. - * + * * @param account * @param teamName * @param memberNames @@ -154,11 +156,11 @@ private void printAccount(PrintStream out, Account account) { out.format("%22s", " "); out.print("'" + account.getDisplayName() + "' "); - + // if (contest.isAllowed (edu.csus.ecs.pc2.core.security.Permission.Type.VIEW_PASSWORDS)) { out.print("password '" + account.getPassword() + "' "); // } - + Permission.Type type = Permission.Type.LOGIN; if (account.isAllowed(type)) { out.print(type + " "); @@ -171,6 +173,7 @@ private void printAccount(PrintStream out, Account account) { out.format("%22s", " "); out.print("alias '" + account.getAliasName() + "' "); +// This is probably commented out because there is no "contest" available. Lazy? -- JB // ElementId groupId = account.getGroupId(); // if (groupId != null) { // Group group = contest.getGroup(groupId); @@ -206,5 +209,5 @@ private String join(String delimit, String[] names) { } return buffer.toString(); } - + } diff --git a/test/edu/csus/ecs/pc2/core/model/InternalContestTest.java b/test/edu/csus/ecs/pc2/core/model/InternalContestTest.java index 116735391..771e03424 100644 --- a/test/edu/csus/ecs/pc2/core/model/InternalContestTest.java +++ b/test/edu/csus/ecs/pc2/core/model/InternalContestTest.java @@ -1,8 +1,9 @@ -// 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.core.model; import java.io.File; import java.util.Arrays; +import java.util.HashSet; import java.util.Vector; import edu.csus.ecs.pc2.core.IStorage; @@ -20,14 +21,14 @@ /** * Tests for InternalContest. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ // $HeadURL$ public class InternalContestTest extends AbstractTestCase { - + private boolean debugMode = false; private static final String CONFIG_ACCOUNTS = "ACCOUNTS"; @@ -71,36 +72,40 @@ public class InternalContestTest extends AbstractTestCase { private static final String CONFIG_CONTEST_PASSWORD = "CONTEST_PASSWORD"; private static final String CONFIG_CLIENTID = "CLIENTID"; - + private SampleContest sampleContest = new SampleContest(); - + public InternalContestTest(String name) { super(name); } + @Override public boolean isDebugMode() { return debugMode; } + @Override public void setDebugMode(boolean debugMode) { this.debugMode = debugMode; } + @Override protected void setUp() throws Exception { super.setUp(); } + @Override protected void tearDown() throws Exception { super.tearDown(); } - + protected IStorage createStorage(String name, int siteNumber){ Profile profile = new Profile(name); profile.setSiteNumber(siteNumber); String testdirName = getOutputDataDirectory() + File.separator + profile.getProfilePath(); return new FileStorage(testdirName); } - + /** * Test that General is a default category. */ @@ -108,21 +113,21 @@ public void testGeneralCategoryCreation() { IInternalContest contest = new InternalContest(); int siteNumber = 3; contest.setSiteNumber(siteNumber); - + contest.setStorage(createStorage("testGeneral", siteNumber)); - + contest.initializeStartupData(siteNumber); contest.initializeSubmissions(siteNumber); - + Category[] categories = contest.getCategories(); assertEquals("No categories expected", 0, categories.length); - + // normally this is done when the 1st server logins in contest.setupDefaultCategories(); categories = contest.getCategories(); - + assertEquals("Missing general category", 1, categories.length); - + Category defaultCat = categories[0]; assertEquals("Default category not General", "General", defaultCat.getDisplayName()); @@ -134,19 +139,19 @@ public void testGeneralCategoryCreation() { /** * Simple clone test. - * + * * @throws Exception */ public void testClone() throws Exception { InternalContest contest1 = new InternalContest(); InternalContest contest2 = new InternalContest(); - + int siteNumber = 4; ClientId serverId = new ClientId(siteNumber, Type.SERVER, 0); contest1.setClientId(serverId); contest1.setSiteNumber(siteNumber); - + contest2.setClientId(contest1.getClientId()); contest2.setSiteNumber(siteNumber); @@ -159,30 +164,30 @@ public void testClone() throws Exception { contestsEqual("testClone identical", contest1, contest2, false); } - + /** - * + * */ public void testCloneJudgementsBug849() throws Exception { - + InternalContest contest1 = new InternalContest(); InternalContest contest2 = new InternalContest(); - + int siteNumber = 4; ClientId serverId = new ClientId(siteNumber, Type.SERVER, 0); contest1.setClientId(serverId); contest1.setSiteNumber(siteNumber); - + contest2.setClientId(contest1.getClientId()); contest2.setSiteNumber(siteNumber); contestsEqual("testClone identical", contest1, contest2, true); - + IInternalContest standardContest = new SampleContest().createStandardContest(); - + contestsEqual("Expecting contests to be different", contest1, standardContest, false); } - + protected Profile createProfile (String name){ Profile profile = new Profile(name); profile.setDescription("Contest "+name); @@ -196,7 +201,7 @@ public void testCloneComplex() throws Exception { Profile profile4 = createProfile("Profile 4"); Profile origProfile = new Profile("Orig profile"); ProfileCloneSettings cloneSettings = new ProfileCloneSettings("clone4", "new title", contest3.getContestPassword().toCharArray(), origProfile); - + IInternalContest contest4 = contest3.clone(contest3, profile4, cloneSettings); /** @@ -205,10 +210,10 @@ public void testCloneComplex() throws Exception { contestsEqual("testCloneComplex", contest3, contest4, true); } - + /** * Create profile directory and security/encryption information. - * + * * @param profile * @param password * @throws FileSecurityException @@ -217,22 +222,22 @@ public void testCloneComplex() throws Exception { private void createProfileFilesAndDirs(Profile profile, String password) throws FileSecurityException { String profileDirectory = profile.getProfilePath(); - + if (new File(profileDirectory).isDirectory()){ new Exception("Directory already exists: "+profileDirectory); } - + new File(profileDirectory).mkdirs(); - + FileSecurity fileSecurity = new FileSecurity(profileDirectory); fileSecurity.saveSecretKey(password.toCharArray()); } - + /** * Test cloning of a clone. - * + * * Positive test. - * + * * @throws Exception */ public void testDoubleClone() throws Exception { @@ -261,7 +266,7 @@ public void testDoubleClone() throws Exception { contestsEqual("testCloneComplete", contest1, contest3, true); } - + public void testRunsClone() throws Exception { String password = "foo"; @@ -299,10 +304,10 @@ public void testRunsClone() throws Exception { /** * Test clone with no runs */ - + Run[] runs = newContest.getRuns(); assertEquals("Runs created", runs.length, 0); - + /** * Test clone with 5 (numRuns) runs. */ @@ -325,11 +330,11 @@ public void testRunsClone() throws Exception { } /** * Compares the contests and returns differences. - * + * * Each line starts with the config area followed by a colon. ex. JUDGEMENTS: or LANGUAGES: *

    * The config areas are constants starting with CONFIG_ in this class. - * + * * @param contest1 * @param contest2 * @return @@ -381,7 +386,7 @@ public String[] rawCompareContests(IInternalContest contest1, IInternalContest c failures.addElement(CONFIG_CONTEST_PASSWORD + ": no_match"); failures = new Vector(); - + if (! contest1.getClientId().equals(contest2.getClientId())) { failures.addElement(CONFIG_CLIENTID + ": " + contest1.getClientId() + " vs " + contest2.getClientId()); } @@ -438,12 +443,12 @@ public String[] rawCompareContests(IInternalContest contest1, IInternalContest c failures.addElement(CONFIG_GROUPS + ": > " + group + " " + group.getElementId()); } } - + Judgement[] judgements = contest2.getJudgements(); for (Judgement judgement : judgements) { Judgement otherJudgement = contest1.getJudgement(judgement.getElementId()); - + if (otherJudgement == null) { failures.addElement(CONFIG_JUDGEMENTS + ": < " + judgement + " " + judgement.getElementId()); } else { @@ -469,7 +474,7 @@ public String[] rawCompareContests(IInternalContest contest1, IInternalContest c failures.addElement(CONFIG_ACCOUNTS + ": < " + account + " " + account.getElementId()); } else { compare(failures, CONFIG_ACCOUNTS, "Display Name", account.getDisplayName(), otherAccount.getDisplayName()); - compare(failures, CONFIG_ACCOUNTS, "Group", account.getGroupId(), otherAccount.getGroupId()); + compare(failures, CONFIG_ACCOUNTS, "Groups", account.getGroupIds(), otherAccount.getGroupIds()); compare(failures, CONFIG_ACCOUNTS, "Alias", account.getAliasName(), otherAccount.getAliasName()); // TODO all the rest of the account fields. @@ -486,8 +491,8 @@ public String[] rawCompareContests(IInternalContest contest1, IInternalContest c failures.addElement(CONFIG_ACCOUNTS + ": < " + account + " " + account.getElementId()); } } - - String[] failureList = (String[]) failures.toArray(new String[failures.size()]); + + String[] failureList = failures.toArray(new String[failures.size()]); // if (failureList.length > 0) { // for (String string : failureList) { // System.out.println(string); @@ -498,6 +503,32 @@ public String[] rawCompareContests(IInternalContest contest1, IInternalContest c } + /** + * Add an error string to the failures vector if the sets are not equal. + * + * @param failures vector of current failures + * @param contestConfigArea what part of the contests are being compared + * @param comment more detailed comment + * @param set1 first set + * @param set2 second set + */ + private void compare(Vector failures, String contestConfigArea, String comment, HashSet set1, HashSet set2) { + if (set1 == null && set2 == null) { + return; + } + + if(set1 == null) { + if(set2 == null) { + return; + } + failures.add(contestConfigArea + ": " + comment + " (null vs " + set2.toString() + ")"); + } else if(set2 == null) { + failures.add(contestConfigArea + ": " + comment + " (" + set1.toString() + " vs " + "null)"); + } else if(!set1.equals(set2)) { + failures.add(contestConfigArea + ": " + comment + " (" + set1.toString() + " vs " + set2.toString() + ")"); + } + } + private void compare(Vector failures, String contestConfigArea, String comment, ElementId elementId, ElementId elementId2) { if (elementId == null && elementId2 == null) { @@ -518,18 +549,18 @@ private void compare(Vector failures, String contestConfigArea, String c private void compare(Vector vector, String contestConfigArea, String comment, String string1, String string2) { - if (string2 == null) { + if (string1 == null) { vector.add(contestConfigArea + ": " + comment + " (null" + " vs " + string2 + ")"); } else if (!string1.equals(string2)) { vector.add(contestConfigArea + ": " + comment + " (" + string1 + " vs " + string2 + ")"); } } - + private void compare(Vector vector, String contestConfigArea, String comment, int int1, int int2) { String string1 = Integer.toString(int1); String string2 = Integer.toString(int2); - + compare(vector, contestConfigArea, comment, string1, string2); } @@ -537,30 +568,30 @@ private void compare(Vector vector, String contestConfigArea, String com String string1 = Boolean.toString(boolean1); String string2 = Boolean.toString(boolean2); - + compare(vector, contestConfigArea, comment, string1, string2); } /** * Ensure judgement acronyms are automatically created. - * + * * @throws Exception */ public void testJudgementAcronymsPopulated() throws Exception { String testDirectory = getOutputDataDirectory(); - + Log log = createLog(getName()); - + StaticLog.setLog(log); - + InternalContest contest = new InternalContest(); contest.setContestPassword(getName()); - + Profile profile = new Profile (getName()); profile.setProfilePath(testDirectory); - + InternalController internalController = new InternalController(contest); internalController.setContactingRemoteServer(false); internalController.setLog(log); @@ -568,23 +599,23 @@ public void testJudgementAcronymsPopulated() throws Exception { // internalController.addConsoleLogging(); internalController.setUsingGUI(false); internalController.initializeServer(contest); - + Judgement[] judgements = contest.getJudgements(); - + assertEquals("Expecting judgement count ", 8, judgements.length); - + for (Judgement judgement : judgements) { // System.out.println(" judgements "+judgement+" "+judgement.getAcronym()); assertNotNull("Expected acronym "+judgement.getAcronym()); } - + } - + /** * Compare InternContests. - * - * Fails assert if contest are not equal and expectingSame is true. - * + * + * Fails assert if contest are not equal and expectingSame is true. + * * @param comment * @param contest1 * @param contest2 @@ -593,12 +624,12 @@ public void testJudgementAcronymsPopulated() throws Exception { private void contestsEqual(String comment, IInternalContest contest1, IInternalContest contest2, boolean expectingSame) { String[] differences = rawCompareContests(contest1, contest2); if (differences.length > 0) { - + if (expectingSame){ System.out.println("There were differences in: '" + comment + "' use debugMode to see details."); // new Exception("There were differences in: '" + comment + "'").printStackTrace(); } - + if (debugMode){ for (String line : differences) { System.err.println(line); @@ -610,7 +641,7 @@ private void contestsEqual(String comment, IInternalContest contest1, IInternalC assertTrue("Contests NOT the same " + comment, differences.length == 0); } } - + String toString(ContestTime contestTime) { StringBuffer buffer = new StringBuffer().append(", getConestLengthMins=" + contestTime.getContestLengthMins()) // diff --git a/test/edu/csus/ecs/pc2/core/model/ProblemTest.java b/test/edu/csus/ecs/pc2/core/model/ProblemTest.java index 5fc222a04..6a7d51c81 100644 --- a/test/edu/csus/ecs/pc2/core/model/ProblemTest.java +++ b/test/edu/csus/ecs/pc2/core/model/ProblemTest.java @@ -2,6 +2,8 @@ package edu.csus.ecs.pc2.core.model; import java.io.File; +import java.util.ArrayList; +import java.util.List; import edu.csus.ecs.pc2.core.Constants; import edu.csus.ecs.pc2.core.model.Problem.VALIDATOR_TYPE; @@ -9,11 +11,12 @@ /** * Test for Problem class. - * + * * @author pc2@ecs.csus.edu */ public class ProblemTest extends AbstractTestCase { + @Override protected void setUp() throws Exception { super.setUp(); @@ -21,7 +24,7 @@ protected void setUp() throws Exception { /** * Get a new populated Problem - * + * * @return Problem */ private Problem getProblemAnew() { @@ -35,7 +38,7 @@ private Problem getProblemAnew() { p2.getPC2ValidatorSettings().setWhichPC2Validator(3); p2.getPC2ValidatorSettings().setIgnoreCaseOnValidation(true); - p2.setOutputValidatorCommandLine(Constants.DEFAULT_PC2_VALIDATOR_COMMAND + " -pc2 " + p2.getWhichPC2Validator() + p2.setOutputValidatorCommandLine(Constants.DEFAULT_PC2_VALIDATOR_COMMAND + " -pc2 " + p2.getWhichPC2Validator() + " " + p2.getPC2ValidatorSettings().isIgnoreCaseOnValidation()); p2.setOutputValidatorProgramName(Constants.PC2_VALIDATOR_NAME); @@ -46,9 +49,9 @@ private Problem getProblemAnew() { p2.setLetter("F"); p2.setSiteNumber(4); - + assertTrue("Expecting that all can view this problem ",p2.isAllView()); - + return p2; } @@ -152,7 +155,7 @@ public void testIsSameIs() { p2 = getProblemAnew(); p2.getPC2ValidatorSettings().setIgnoreCaseOnValidation(false); - checkBoolean("setIgnoreSpacesOnValidation", p1.getPC2ValidatorSettings().isIgnoreCaseOnValidation(), + checkBoolean("setIgnoreSpacesOnValidation", p1.getPC2ValidatorSettings().isIgnoreCaseOnValidation(), p2.getPC2ValidatorSettings().isIgnoreCaseOnValidation(), p1, p2); p2 = getProblemAnew(); @@ -271,86 +274,94 @@ public void testDefaultTimeout() throws Exception { assertEquals("Time limit", 10, problem.getTimeOutInSeconds()); } - + public void testDefaultMaxOutputKB() throws Exception { Problem problem = new Problem("Bar"); assertEquals("Max output", Problem.DEFAULT_MAX_OUTPUT_FILE_SIZE_KB, problem.getMaxOutputSizeKB()); } - + /** * Test isSameas for balloon color and rgb. - * + * * @throws Exception */ public void testSameAsBalloonColors() throws Exception { - + Problem p1 = getProblemAnew(); Problem p2 = getProblemAnew(); assertTrue("Is same As", p1.isSameAs(p2)); - + p1.setColorName("White"); assertFalse("Expecting diff from color", p1.isSameAs(p2)); - + p1 = getProblemAnew(); p1.setColorRGB("#444"); assertFalse("Expecting diff from RGB", p1.isSameAs(p2)); - + assertFalse("Is not same As, null parameter", p1.isSameAs(null)); - + } - + /** * Test adding groups to a problem. - * + * * @throws Exception */ public void testAddGroup() throws Exception { - + SampleContest sample =new SampleContest(); IInternalContest contest = sample.createStandardContest(); sample.assignSampleGroups(contest, "Group Thing One", "Group Thing Two"); - + Problem[] problems = contest.getProblems(); - - Problem lastProbblem = problems[problems.length - 1]; + + Problem lastProblem = problems[problems.length - 1]; Group[] groups = contest.getGroups(); - - - Group group1 = groups[0]; - + assertEquals("Number groups ", 2, groups.length); - - assertTrue (lastProbblem.isAllView()); - lastProbblem.addGroup(group1); - - assertTrue (lastProbblem.canView(group1)); - assertFalse (lastProbblem.canView(null)); - assertFalse (lastProbblem.canView(groups[1])); - - lastProbblem.clearGroups(); - assertTrue (lastProbblem.isAllView()); - + + Group group1 = groups[0]; + + ArrayList arrayOfGroup1 = new ArrayList(); + arrayOfGroup1.add(group1); + ArrayList arrayOfGroup2 = new ArrayList(); + arrayOfGroup2.add(groups[1]); + + assertTrue (lastProblem.isAllView()); + lastProblem.addGroup(group1); + + assertTrue (lastProblem.canView(group1)); + assertFalse (lastProblem.canView((Group)null)); + // a null wanted group list means "any group" + assertTrue (lastProblem.canView((List)null)); + assertTrue (lastProblem.canView(arrayOfGroup1)); + assertFalse (lastProblem.canView(arrayOfGroup2)); + assertFalse (lastProblem.canView(groups[1])); + + lastProblem.clearGroups(); + assertTrue (lastProblem.isAllView()); + Account[] teams = SampleContest.getTeamAccounts(contest); - - Group teamGroup = contest.getGroup(teams[0].getGroupId()); - - lastProbblem.addGroup(teamGroup); - assertTrue (lastProbblem.canView(teamGroup)); - + + Group teamGroup = contest.getGroup(teams[0].getPrimaryGroupId()); + + lastProblem.addGroup(teamGroup); + assertTrue (lastProblem.canView(teamGroup)); + Problem middleProblem = problems[problems.length / 2]; for (Group group : groups) { middleProblem.addGroup(group); } - + assertFalse (middleProblem.isAllView()); for (Group group : groups) { assertTrue (middleProblem.canView(group)); } - - + + // I wonder what this was for? -- JB // ProblemsReport report = new ProblemsReport(); // IInternalController controller = sample.createController(contest, true, false); // report.setContestAndController(contest, controller); diff --git a/test/edu/csus/ecs/pc2/core/model/SampleContest.java b/test/edu/csus/ecs/pc2/core/model/SampleContest.java index 912d63aa4..f6cf20197 100644 --- a/test/edu/csus/ecs/pc2/core/model/SampleContest.java +++ b/test/edu/csus/ecs/pc2/core/model/SampleContest.java @@ -1,4 +1,4 @@ -// 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.core.model; import java.io.File; @@ -41,12 +41,12 @@ /** * Create Sample contest and controller. - * + * * Create contest and controller and add various data values. - * + * * @see #createContest(int, int, int, int) * @see #createController(IInternalContest, boolean, boolean) - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -65,7 +65,7 @@ public class SampleContest { private NotificationUtilities notificationUtilities = new NotificationUtilities(); private String samplesDirectory; - + public static final String DEFAULT_INTERNATIONAL_VALIDATOR_COMMAND = "{:validator} {:infile} {:outfile} {:ansfile} {:resfile} "; private static final String[] W3C_COLORS = { "Alice Blue;F0F8FF", "Antique White;FAEBD7", "Aqua;00FFFF", "Aquamarine;7FFFD4", "Azure;F0FFFF", "Beige;F5F5DC", "Bisque;FFE4C4", "Black;000000", @@ -85,7 +85,7 @@ public class SampleContest { "Sandy Brown;F4A460", "Sea Green;2E8B57", "Seashell;FFF5EE", "Sienna;A0522D", "Silver;C0C0C0", "Sky Blue;87CEEB", "Slate Blue;6A5ACD", "Slate Gray;708090", "Snow;FFFAFA", "Spring Green;00FF7F", "Steel Blue;4682B4", "Tan;D2B48C", "Teal;008080", "Thistle;D8BFD8", "Tomato;FF6347", "Turquoise;40E0D0", "Violet;EE82EE", "Wheat;F5DEB3", "White;FFFFFF", "White Smoke;F5F5F5", "Yellow;FFFF00", "Yellow Green;9ACD32" }; - + public static final String[] GIRL_NAMES = { "Abigail", "Aimee", "Alexandra", "Alice", "Alisha", "Amber", "Amelia", "Amelie", "Amy", "Anna", "Ava", "Bethany", "Brooke", "Caitlin", "Charlotte", "Chloe", "Daisy", "Eleanor", "Elizabeth", "Ella", "Ellie", "Emilia", "Emily", "Emma", "Erin", "Esme", "Eva", "Eve", "Evelyn", "Evie", "Faith", "Florence", "Francesca", "Freya", "Georgia", "Grace", "Gracie", "Hannah", "Harriet", "Heidi", "Hollie", "Holly", "Imogen", "Isabel", "Isabella", "Isabelle", "Isla", "Isobel", "Jasmine", "Jessica", "Julia", "Katie", "Keira", "Lacey", @@ -107,7 +107,7 @@ public SampleContest() { /** * Create a new Site class instance. - * + * * @param siteNumber * site number * @param siteName @@ -146,9 +146,9 @@ public IInternalContest createContest(int siteNumber, int numSites, int numTeams /** * Create an instance of contest with languages, problems, judgements, teams and judges. - * + * * @param siteNumber current site number - * @param numSites + * @param numSites * @param numTeams * @param numJudges * @param initAsServer initialize as server, else initalizes as admin. @@ -163,11 +163,11 @@ public IInternalContest createContest(int siteNumber, int numSites, int numTeams String[] judgements = { "Stupid programming error", "Misread problem statement", "Almost there", "You have no clue", "Give up and go home", "Consider switching to another major", "How did you get into this place ?", "Contact Staff - you have no hope" }; String[] acronymns = {"CE", "WA", "TLE", "WA2", "RTE", "OFE", "WA3", "JE" }; - + InternalContest contest = new InternalContest(); contest.setSiteNumber(siteNumber); - + for (int i = 0; i < numSites; i++) { Site site = createSite(i + 1, "Site " + (i + 1), null, 0); contest.addSite(site); @@ -191,7 +191,7 @@ public IInternalContest createContest(int siteNumber, int numSites, int numTeams } contest.addLanguage(language); } - + Problem[] problems = createProblems(problemsNames, 'A', siteNumber); for (Problem problem : problems) { contest.addProblem(problem); @@ -209,7 +209,7 @@ public IInternalContest createContest(int siteNumber, int numSites, int numTeams contest.addJudgement(judgement); i++; } - + contest.generateNewAccounts(Type.ADMINISTRATOR.toString(), 1, true); if (numTeams > 0) { @@ -267,10 +267,10 @@ public Problem[] createProblems(String [] problemNames, char startLetter, int si } return problems; } - + /** * Assign colors. - * + * * @param contest */ public void assignColors(IInternalContest contest) { @@ -294,7 +294,7 @@ public void assignColors(IInternalContest contest) { /** * Returns 6 character hex RGB string for input color name. - * + * * @param colorName */ public String lookupRGB(String colorName) { @@ -336,7 +336,7 @@ public IInternalController createController(IInternalContest contest, boolean is /** * Create a InternalController. - * + * * @param contest * model for controller * @param isServer @@ -352,8 +352,8 @@ public IInternalController createController(IInternalContest contest, String sto // Start site 1 InternalController controller = new InternalController(contest); controller.setUsingMainUI(false); - - controller.setLog(null); // creates and opens a default log name + + controller.setLog(null); // creates and opens a default log name if (isServer) { controller.setContactingRemoteServer(isRemote); @@ -379,7 +379,7 @@ public IInternalController createController(IInternalContest contest, String sto return controller; } - + /** * Create a controller which saves packets. */ @@ -412,8 +412,8 @@ public InternalControllerSpecial createPacketController(IInternalContest contest return controller; } - - + + public static String getTestDirectoryName(String subDirName) { String testDir = "testout" + File.separator + subDirName; @@ -426,7 +426,7 @@ public static String getTestDirectoryName(String subDirName) { /** * Populate Language, Problems and Judgements. - * + * * @param contest */ public void populateContest(IInternalContest contest) { @@ -464,30 +464,30 @@ public void populateContest(IInternalContest contest) { /** * Get all sites' teams. - * + * * @param contest * @return */ public static Account[] getTeamAccounts(IInternalContest contest) { return getAccounts(contest, Type.TEAM); } - + public Account[] getJudgeAccounts(IInternalContest contest) { return getAccounts(contest, Type.JUDGE); } - - + + /** * Get site's teams. - * + * * @param contest * @param siteNumber * @return */ public static Account[] getTeamAccounts(IInternalContest contest, int siteNumber) { Vector accountVector = contest.getAccounts(ClientType.Type.TEAM, siteNumber); - Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + Account[] accounts = accountVector.toArray(new Account[accountVector.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; @@ -495,7 +495,7 @@ public static Account[] getTeamAccounts(IInternalContest contest, int siteNumber /** * Create N runs identical to input run, add elapsed time. - * + * * @param contest * @param numberRuns * @param run @@ -522,8 +522,8 @@ public Run[] cloneRun(IInternalContest contest, int numberRuns, Run run) { return runs; } - - + + public Run[] createRandomRuns(IInternalContest contest, int numberRuns, boolean randomTeam, boolean randomProblem, boolean randomLanguage, int siteNumber) { Account[] accounts = getTeamAccounts(contest); @@ -538,11 +538,11 @@ public Run[] createRandomRuns(IInternalContest contest, int numberRuns, boolean } ClientId teamId = accounts[0].getClientId(); - + return createRandomRuns(contest, numberRuns, teamId, randomTeam, randomProblem, randomLanguage, siteNumber); } - + public Run[] createRandomRuns(IInternalContest contest, int numberRuns, ClientId teamId, boolean randomTeam, boolean randomProblem, boolean randomLanguage, int siteNumber) { Run[] runs = new Run[numberRuns]; @@ -564,16 +564,16 @@ public Run[] createRandomRuns(IInternalContest contest, int numberRuns, ClientId for (int i = 0; i < numberRuns; i++) { Problem problem = problems[0]; Language language = languages[0]; - + if (randomLanguage) { int randomLangIndex = random.nextInt(languages.length); - language = (Language) languages[randomLangIndex]; + language = languages[randomLangIndex]; } if (randomProblem) { int randomProblemIndex = random.nextInt(problems.length); - problem = (Problem) problems[randomProblemIndex]; + problem = problems[randomProblemIndex]; } if (randomTeam) { @@ -593,7 +593,7 @@ public Run[] createRandomRuns(IInternalContest contest, int numberRuns, ClientId /** * Generate a number of new runs. - * + * * @param contest * @param numberRuns * @param randomTeam @@ -607,7 +607,7 @@ public Run[] createRandomRuns(IInternalContest contest, int numberRuns, boolean /** * Create new run and add to contest run list. - * + * * @param contest * @param clientId * @param language @@ -635,7 +635,7 @@ public Run createRun(IInternalContest contest, ClientId clientId, Language langu /** * Create new run and add to contest run list. - * + * * @param contest * @param clientId * @param problem @@ -658,9 +658,9 @@ public Run createRun(IInternalContest contest, ClientId clientId, Problem proble /** * Create a copy of the run (not a clone, but close) and add to contest run list. - * + * * This references the input run's JugementRecords instead of cloning them. This run will have a different getElementId() and a getNumber() which represents the next run number. - * + * * @param contest * @param run * @param cloneJudgements @@ -676,7 +676,7 @@ public Run copyRun(IInternalContest contest, Run run, boolean cloneJudgements) t newRun.setNumber(contest.getRuns().length); newRun.setStatus(RunStates.NEW); newRun.setEntryPoint("Foo.class"); - + if (cloneJudgements) { for (JudgementRecord judgementRecord : newRun.getAllJudgementRecords()) { newRun.addJudgement(judgementRecord); @@ -724,7 +724,7 @@ public Site[] createSites(IInternalContest contest, int count) { /** * Create profiles but do not add profiles to contest. - * + * * @param contest * @param count * @return @@ -742,9 +742,9 @@ public Profile[] createProfiles(IInternalContest contest, int count) { /** * add a judged run to list of runs in a contest. - * + * * Fields in runInfoLine: - * + * *

          * 0 - run id, int
          * 1 - team id, int
    @@ -752,24 +752,24 @@ public Profile[] createProfiles(IInternalContest contest, int count) {
          * 3 - elapsed, int
          * 4 - solved, String "Yes" or No (or full No judgement text)
          * 5 - send to teams, Yes or No
    -     * 
    +     *
          * Example:
          * "6,5,A,12,Yes"
          * "6,5,A,12,Yes,Yes"
    -     * 
    +     *
          * 
    - * + * * @param contest * @param runInfoLine * @throws FileSecurityException * @throws ClassNotFoundException * @throws IOException - * @throws RunUnavailableException + * @throws RunUnavailableException */ public Run addARun(IInternalContest contest, String runInfoLine) throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { // get last judge - Account[] accounts = (Account[]) contest.getAccounts(Type.JUDGE).toArray(new Account[contest.getAccounts(Type.JUDGE).size()]); + Account[] accounts = contest.getAccounts(Type.JUDGE).toArray(new Account[contest.getAccounts(Type.JUDGE).size()]); ClientId judgeId = accounts[accounts.length - 1].getClientId(); Problem[] problemList = contest.getProblems(); @@ -802,11 +802,11 @@ public Run addARun(IInternalContest contest, String runInfoLine) throws IOExcept run.setSiteNumber(contest.getSiteNumber()); run.setElapsedMins(elapsed); run.setEntryPoint("Foo.class"); - + JudgementRecord judgementRecord = null; if (data[4].trim().length() != 0 && (! data[4].equalsIgnoreCase("New"))){ - + // Use a default No entry ElementId judgementId = noJudgement.getElementId(); @@ -822,12 +822,12 @@ public Run addARun(IInternalContest contest, String runInfoLine) throws IOExcept } } } - + judgementRecord = new JudgementRecord(judgementId, judgeId, solved, false); judgementRecord.setSendToTeam(sendToTeams); } - + contest.addRun(run); if (judgementRecord != null){ @@ -850,7 +850,7 @@ private boolean isSolved(IInternalContest contest, ElementId judgementId) { /** * Add a judgement to a run. - * + * * @param contest * @param run * @param judgement @@ -859,7 +859,7 @@ private boolean isSolved(IInternalContest contest, ElementId judgementId) { * @throws IOException * @throws ClassNotFoundException * @throws FileSecurityException - * @throws RunUnavailableException + * @throws RunUnavailableException */ public Run addJudgement(IInternalContest contest, Run run, Judgement judgement, ClientId judgeId) throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { @@ -875,22 +875,22 @@ public Run addJudgement(IInternalContest contest, Run run, Judgement judgement, } public static void checkOutRun(IInternalContest contest, Run run, ClientId judgeId) throws RunUnavailableException, IOException, ClassNotFoundException, FileSecurityException { - + if (run == null) { throw new IllegalArgumentException("run is null"); } - + if (judgeId == null) { throw new IllegalArgumentException("judge id is null"); } - + contest.checkoutRun(run, judgeId, false, false); } - + /** * Assign a color to a problem. - * + * * @param contest * @param problem * @param color @@ -910,7 +910,7 @@ public void assignColor(IInternalContest contest, Problem problem, String color, /** * Add a ballon notification. - * + * * @param contest * @param scoreboardClientId * @param run @@ -938,7 +938,7 @@ private static int getIntegerValue(String s) { /** * Add runs to contest (via acceptRun) - * + * * @param contest * @param runs * @param filename @@ -959,14 +959,14 @@ public void addRuns(IInternalContest contest, Run[] runs, String filename) throw contest.acceptRun(run, runFiles); } } - + /** * Create a sample RunFiles. - * + * * Uses {@link #getSampleFile()} as the filename. - * + * * @param run - * @return + * @return */ public RunFiles createSampleRunFiles (Run run) { return new RunFiles(run, getSampleFile()); @@ -974,7 +974,7 @@ public RunFiles createSampleRunFiles (Run run) { /** * Print the report to the filename. - * + * * @param filename * @param selectedReport * @param filter @@ -999,7 +999,7 @@ public void printReport(String filename, IReport selectedReport, Filter filter, } /** - * + * * @param contest * @return Yes/Solved judgement */ @@ -1029,7 +1029,7 @@ public Judgement getRandomJudgement(IInternalContest contest, boolean solved) { /** * Return Sumit source file in test area. - * + * * @see #getSampleFile(String) * @return */ @@ -1039,9 +1039,9 @@ public String getSampleFile() { /** * Return full filename in file test directory. - * + * * Will print Exceptions if test directory is not present or if no such filename found. - * + * * @param filename * @return filename with path to test data. */ @@ -1054,7 +1054,7 @@ public String getSampleFile(String filename) { } samplesDirectory = projectPath + File.separator + samps + File.separator; } - + String testfilename = samplesDirectory + File.separator + "src" + File.separator + filename; if (!new File(testfilename).isFile()) { @@ -1065,9 +1065,9 @@ public String getSampleFile(String filename) { /** * Create a notification if needed. - * + * * Checks for existing notification, only creates a notification if no notification exists. - * + * * @param contest2 * @param run */ @@ -1082,7 +1082,7 @@ public void createNotification(IInternalContest contest2, Run run) { /** * Unconditionally creates a notification. - * + * * @param contest2 * @param run */ @@ -1097,10 +1097,10 @@ private void addNotification(IInternalContest contest2, Run run) { settings.setBalloonList(balloonList); contest2.updateClientSettings(settings); } - + /** * Assigns two group names to all teams. - * + * * @param contest * @param groupNameOne first half of teams assigned this group * @param groupNameTwo second half of teams assigned this group @@ -1129,15 +1129,15 @@ public void assignTeamExternalIds(IInternalContest contest, int startId) { } } } - + /** * Quick load of contest with runs and groups. - * + * * @param contest * @throws IOException * @throws ClassNotFoundException * @throws FileSecurityException - * @throws RunUnavailableException + * @throws RunUnavailableException */ public IInternalContest quickLoad(IInternalContest contest, int numberOfRuns) throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { @@ -1162,9 +1162,9 @@ public IInternalContest quickLoad(IInternalContest contest, int numberOfRuns) th assignTeamGroup(contest, group1, 0, teams.length / 2); assignTeamGroup(contest, group2, teams.length / 2, teams.length - 1); - + Run[] runs = sample.createRandomRuns(contest, numberOfRuns, true, true, true); - + for (Run run : runs) { contest.addRun(run); } @@ -1191,7 +1191,7 @@ public IInternalContest quickLoad(IInternalContest contest, int numberOfRuns) th /** * Assign group to team startIdx to endIdx. - * + * * @param group * @param startIdx * @param endIdx @@ -1200,7 +1200,7 @@ public IInternalContest quickLoad(IInternalContest contest, int numberOfRuns) th public void assignTeamGroup(IInternalContest contest2, Group group, int startIdx, int endIdx) { Account[] teams = getTeamAccounts(contest2); for (int i = startIdx; i < endIdx; i++) { - teams[i].setGroupId(group.getElementId()); + teams[i].addGroupId(group.getElementId(), true); } } @@ -1233,7 +1233,7 @@ public String createSampleAnswerFile(String filename) throws FileNotFoundExcepti /** * Write array to PrintWriter. - * + * * @param writer * @param datalines */ @@ -1245,7 +1245,7 @@ public void writeLines(PrintWriter writer, String[] datalines) { /** * Add and Write sample judge's data and answer file for problem. - * + * * @param contest * @param problem * @throws FileNotFoundException @@ -1274,7 +1274,7 @@ void addDataFiles(IInternalContest contest, String testDirectory, Problem proble contest.updateProblem(problem, dataFiles); } - + public String createSumitDataFile(String filename) throws FileNotFoundException { PrintWriter writer = new PrintWriter(new FileOutputStream(filename, false), true); String[] datalines = { "25", "50", "-25", "0" }; @@ -1288,7 +1288,7 @@ public String createSumitDataFile(String filename) throws FileNotFoundException } /** - * + * * @param filename * @return * @throws FileNotFoundException @@ -1306,24 +1306,24 @@ public String createSumitAnswerFile(String filename) throws FileNotFoundExceptio } public void addInternalValidator(IInternalContest contest, Problem problem, int optionNumber) { - + problem.setValidatorType(VALIDATOR_TYPE.PC2VALIDATOR); - + PC2ValidatorSettings settings = new PC2ValidatorSettings(); settings.setWhichPC2Validator(optionNumber); settings.setIgnoreCaseOnValidation(true); - settings.setValidatorCommandLine(Constants.DEFAULT_PC2_VALIDATOR_COMMAND + " -pc2 " + settings.getWhichPC2Validator() + settings.setValidatorCommandLine(Constants.DEFAULT_PC2_VALIDATOR_COMMAND + " -pc2 " + settings.getWhichPC2Validator() + " " + settings.isIgnoreCaseOnValidation()); settings.setValidatorProgramName(Constants.PC2_VALIDATOR_NAME); problem.setPC2ValidatorSettings(settings); - + contest.updateProblem(problem); } /** * Add and Write sample judge's data and answer file for problem. - * + * * @param contest * @param problem * @param dataFileName @@ -1365,15 +1365,15 @@ public void addDataFiles(IInternalContest contest, String testDirectory, Proble public static Account[] getAccounts(IInternalContest contest, ClientType.Type type) { Vector accountVector = contest.getAccounts(type); - Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + Account[] accounts = accountVector.toArray(new Account[accountVector.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; } - + public Account[] getAccounts(IInternalContest contest, ClientType.Type type, int siteNumber) { Vector accountVector = contest.getAccounts(type, siteNumber); - Account[] accounts = (Account[]) accountVector.toArray(new Account[accountVector.size()]); + Account[] accounts = accountVector.toArray(new Account[accountVector.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; @@ -1381,7 +1381,7 @@ public Account[] getAccounts(IInternalContest contest, ClientType.Type type, int /** * Turn test mode on. - * + * * @param contest */ public void setCCSTestMode(IInternalContest contest) { @@ -1395,7 +1395,7 @@ public void setCCSTestMode(IInternalContest contest) { /** * Set for Internal CCS validation. - * + * * @param contest * @param validatorParameters * @param problem @@ -1404,7 +1404,7 @@ public void setClicsValidation(IInternalContest contest, String validatorParamet problem.setValidatorType(VALIDATOR_TYPE.CLICSVALIDATOR); problem.setOutputValidatorProgramName(Constants.CLICS_VALIDATOR_NAME); - + problem.setReadInputDataFromSTDIN(true); if (validatorParameters == null) { @@ -1416,7 +1416,7 @@ public void setClicsValidation(IInternalContest contest, String validatorParamet /** * Create ISumit class that reads from stdin, basename filename must be ISumit.java. - * + * * @param filename * @return * @throws FileNotFoundException @@ -1463,16 +1463,17 @@ private String[] getSumitStdinSource() { return lines; } - + /** * Creates sumit program, must use filename Sumit.java. - * + * * @deprecated use getSamplesSourceFilename from AbstractTestCase * @param filename source file name. * @param inputFilename file name for program to read * @return name of filename * @throws FileNotFoundException */ + @Deprecated public String createSampleSumitSource(String filename, String inputFilename) throws FileNotFoundException { PrintWriter writer = new PrintWriter(new FileOutputStream(filename, false), true); String[] datalines = getSumitSourceLines(inputFilename); @@ -1481,7 +1482,7 @@ public String createSampleSumitSource(String filename, String inputFilename) thr writer = null; return filename; } - + private String[] getSumitSourceLines(String inputfilename) { String [] lines = { @@ -1516,13 +1517,13 @@ private String[] getSumitSourceLines(String inputfilename) { }; return lines; } - - + + /** * Get problem letter for input integer. - * + * * getProblemLetter(1) is 'A' - * + * * @param id * a one based problem number. * @return @@ -1535,7 +1536,7 @@ public static String getProblemLetter(int id) { /** * Get list of problem letters for filter. - * + * * @param contest * @param filter * @return list of letters joined by ", " @@ -1561,7 +1562,7 @@ public static String getProblemLetters(IInternalContest contest, Filter filter) /** * Join string together. - * + * * @param delimiter * @param list * @return buffer of list joined using delimiter. @@ -1582,21 +1583,21 @@ public static StringBuffer join(String delimiter, List list) { /** * Create a new judged run with a Yes judgement. - * + * * @param contest * @return - * @throws RunUnavailableException - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws RunUnavailableException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ public Run createRandomJudgedRunSolved(IInternalContest contest) throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { - + Run run = createRandomRuns(contest, 1, true, true, true)[0]; ClientId judge = getRandomAccount(contest,Type.JUDGE).getClientId(); Judgement yes = getYesJudgement(contest); contest.addRun(run); - return addJudgement(contest, run, yes, judge); + return addJudgement(contest, run, yes, judge); } private Account getRandomAccount(IInternalContest contest, Type type) { @@ -1604,7 +1605,7 @@ private Account getRandomAccount(IInternalContest contest, Type type) { int randomIndex = random.nextInt(accounts.length); return accounts[randomIndex]; } - + private Account getRandomAccount(IInternalContest contest, Type type, int siteNumber) { Account[] accounts = getAccounts(contest, type, siteNumber); int randomIndex = random.nextInt(accounts.length); @@ -1613,18 +1614,18 @@ private Account getRandomAccount(IInternalContest contest, Type type, int siteNu /** * Add run test cases. - * + * * @param inContest * @param run * @param count */ public void addTestCase(IInternalContest inContest, Run run, int count) { - + JudgementRecord judgementRecord = run.getJudgementRecord(); if (judgementRecord == null){ throw new RuntimeException("Run has no judgement records "+run); } - + for (int i = 0; i < count; i++) { RunTestCase runTestCaseResult = new RunTestCase(run, judgementRecord, i+1, run.isSolved()); run.addTestCase (runTestCaseResult); @@ -1635,14 +1636,14 @@ public IInternalContest createStandardContest() { return createContest(3, 3, 120, 12, true); } - + public String getSampleFileName(String baseFileName) { return "samps" + File.separator + "src" + File.separator + baseFileName; } /** * Create judge data and answer files as internal files. - * + * * @param problem * problem for data files * @param testCases @@ -1674,7 +1675,7 @@ public ProblemDataFiles createProblemDataFiles(Problem problem, int testCases, S /** * Create testCases number of data files (data and answer) as internal files. - * + * * @param problem * problem for data files * @param testCases @@ -1732,11 +1733,11 @@ public void addDataFiles(Problem problem, ProblemDataFiles problemDataFiles, Str /** * Load data files into datafiles and problem from files in dataFileBaseDirectory. - * + * * Will remove test data sets, and add data sets from dataFileBaseDirectory. *
    * Will create new ProblemDataFiles if input files == null. - * + * * @param problem problem for the data files. * @param files input data files (null to create new ProblemDataFiles from scratch) * @param dataFileBaseDirectory directory for data files @@ -1802,7 +1803,7 @@ public static SerializedFile[] createSerializedFiles(String dataFileBaseDirector outfiles.add(new SerializedFile(filename, externalFilesFlag)); } - return (SerializedFile[]) outfiles.toArray(new SerializedFile[outfiles.size()]); + return outfiles.toArray(new SerializedFile[outfiles.size()]); } public void setAliases(IInternalContest inContest) { @@ -1813,7 +1814,7 @@ public void setAliases(IInternalContest inContest) { idx++; } } - + // copied from https://stackoverflow.com/questions/40074840/reading-a-csv-file-into-a-array public static String[] loadStringArrayFromCSV(String fileName) { File file= new File(fileName); @@ -1870,14 +1871,14 @@ public static String[] loadExpectedResultsFromTSV(String filename) { } return lines.toArray(new String[lines.size()]); - + } - + /** * add run to list of runs in a contest. - * + * * Files found in runInfoLine, comma delmited - * + * *
          * 0 - run id, int
          * 1 - team id, int
    @@ -1887,42 +1888,42 @@ public static String[] loadExpectedResultsFromTSV(String filename) {
          * 5 - send to teams, Yes or No
          * 6 - No Judgement index
          * 7 - Validator judgement string
    -     * 
    +     *
          * Example:
          * "6,5,A,12,Yes"
          * "6,5,A,12,Yes,Yes"
    -     * 
    +     *
          * 
    - * + * * @param contest * @param runInfoLine - * @param computerJudged - * @throws Exception - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @param computerJudged + * @throws Exception + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ public static void addRunFromInfo(IInternalContest contest, String runInfoLine, boolean computerJudged) throws Exception { // get 5th judge ClientId judgeId = contest.getAccounts(Type.JUDGE).elementAt(4).getClientId(); - + Problem[] problemList = contest.getProblems(); Language languageId = contest.getLanguages()[0]; Judgement yesJudgement = contest.getJudgements()[0]; Judgement[] judgement = contest.getJudgements(); Judgement noJudgement = null; - + for (int i = 0; i < judgement.length; i++) { if (judgement[i].getAcronym().equals("WA")) { noJudgement = judgement[i]; break; } } - + String[] data = runInfoLine.split(","); - + // Line is: runId,teamId,problemLetter,elapsed,solved[,sendToTeamsYN] int runId = getIntegerValue(data[0]); @@ -1930,7 +1931,7 @@ public static void addRunFromInfo(IInternalContest contest, String runInfoLine, String probLet = data[2]; int elapsed = getIntegerValue(data[3]); boolean solved = data[4].equals("Yes"); - + boolean sendToTeams = true; if (data.length > 5){ sendToTeams = data[5].equals("Yes"); @@ -1938,7 +1939,7 @@ public static void addRunFromInfo(IInternalContest contest, String runInfoLine, if (data.length > 6) { noJudgement = contest.getJudgements()[getIntegerValue(data[6])]; } - + String validatorJudgementString = null; if (data.length > 7) { validatorJudgementString = data[7]; @@ -1979,8 +1980,8 @@ public static void addRunFromInfo(IInternalContest contest, String runInfoLine, public static void addRunFromInfo(IInternalContest contest, String runInfoLine) throws Exception { addRunFromInfo(contest, runInfoLine, false); } - - + + public static void addRunFromInfo(IInternalContest contest, String[] runInfoLines) throws Exception { for (String runInfoLine : runInfoLines) { addRunFromInfo(contest, runInfoLine, false); @@ -1989,9 +1990,9 @@ public static void addRunFromInfo(IInternalContest contest, String[] runInfoLine /** * Assign unique reservation id to account. - * + * * Loops through team accounts and assigned external id (Reservation ID) sequential numbers/ids starting at startId - * + * * @param contest * @param startId * - start with id @@ -2012,7 +2013,7 @@ public Vector generateNewAccounts(IInternalContest contest, Type type, /** * Get account at current site. - * + * * @param contest * @param administrator * @param clientNumber @@ -2021,7 +2022,7 @@ public Vector generateNewAccounts(IInternalContest contest, Type type, public Account getAccount(IInternalContest contest, Type type, int clientNumber) { return contest.getAccount(new ClientId(contest.getSiteNumber(), type, clientNumber)); } - + public String getTestSampleContestDirectory(String dirname) { return getSampleContestsDirectory() + File.separator + dirname; } @@ -2029,7 +2030,7 @@ public String getTestSampleContestDirectory(String dirname) { public String getSampleContestsDirectory() { return "samps" + File.separator + "contests"; } - + /** * Load contest from sample contets * @param contest @@ -2055,7 +2056,7 @@ public IInternalContest loadSampleContest(IInternalContest contest, String sampl throw e; } } - - + + } diff --git a/test/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithmTest.java b/test/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithmTest.java index 1381c8001..ecf9c8bc1 100644 --- a/test/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithmTest.java +++ b/test/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithmTest.java @@ -1,4 +1,4 @@ -// 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.core.scoring; import java.io.File; @@ -46,16 +46,16 @@ /** * Test Scoring Algorithm. - * + * * The inital tests were to insure that proper XML * is created on startup. - * + * * @author pc2@ecs.csus.edu */ public class DefaultScoringAlgorithmTest extends AbstractTestCase { - + private static Log log = null; - + private boolean debugMode = false; // alt1: 0 0 200 0 0 private Properties alt1 = populateProperties(0, 0, 200, 0, 0); @@ -70,6 +70,7 @@ public class DefaultScoringAlgorithmTest extends AbstractTestCase { private File loadData; + @Override protected void setUp() throws Exception { super.setUp(); @@ -91,7 +92,7 @@ protected void setUp() throws Exception { System.err.println("could not find " + loadFile); throw new Exception("Unable to locate "+loadFile); } - + } private Properties populateProperties(int perNo, int perMin, int baseYes, int perCE, int perSV) { @@ -129,7 +130,7 @@ private Properties populateProperties(int perNo, int perMin, int baseYes, int pe public void testNoData() { InternalContest contest = new InternalContest(); - + // Add scoreboard account and set the scoreboard account for this client (in contest) contest.setClientId(createBoardAccount (contest)); @@ -138,9 +139,9 @@ public void testNoData() { /** * Initialize the contest. - * + * * Initialize with problems, languages, accounts, judgements. - * + * * @param contest */ private void initContestData(IInternalContest contest) { @@ -148,36 +149,36 @@ private void initContestData(IInternalContest contest) { // Add accounts contest.generateNewAccounts(ClientType.Type.TEAM.toString(), 1, true); contest.generateNewAccounts(ClientType.Type.TEAM.toString(), 1, true); - + contest.generateNewAccounts(ClientType.Type.JUDGE.toString(), 1, true); // Add scoreboard account and set the scoreboard account for this client (in contest) contest.setClientId(createBoardAccount (contest)); - + // Add Problem Problem problem = new Problem("Problem One"); contest.addProblem(problem); - + // Add Language Language language = new Language("Language One"); contest.addLanguage(language); - + String[] judgementNames = { "Yes", "No - compilation error", "No - incorrect output", "No - It's just really bad", "No - judges enjoyed a good laugh", "You've been bad - contact staff", "No - Illegal Function" }; - + String[] acronyms = { "AC", "CE", "WA", "WA", "WA", "WA", "SV" }; - + for (int i = 0; i < judgementNames.length; i++) { Judgement judgement = new Judgement(judgementNames[i], acronyms[i]); contest.addJudgement(judgement); } - + checkForJudgeAndTeam(contest); } /** * Create and return a new scoreboard client. - * + * * @param contest * @return a ClientId for newly created scoreboard account. */ @@ -188,14 +189,14 @@ private ClientId createBoardAccount(IInternalContest contest) { /** * Insure that there is one team and one judge in the contest model. - * + * * @param contest */ private void checkForJudgeAndTeam(IInternalContest contest) { Account account = contest.getAccounts(ClientType.Type.TEAM).firstElement(); assertFalse("Team account not generated", account == null); assertFalse("Team account not generated", account.getClientId().equals(Type.TEAM)); - + account = contest.getAccounts(ClientType.Type.JUDGE).firstElement(); assertFalse("Judge account not generated", account == null); assertFalse("Team account not generated", account.getClientId().equals(Type.TEAM)); @@ -213,12 +214,12 @@ private void initData(IInternalContest contest, int numTeams, int numProblems) { // Add accounts contest.generateNewAccounts(ClientType.Type.TEAM.toString(), numTeams, true); contest.generateNewAccounts(ClientType.Type.JUDGE.toString(), 6, true); - + // Add scoreboard account and set the scoreboard account for this client (in contest) contest.setClientId(createBoardAccount (contest)); - + checkForJudgeAndTeam(contest); - + // Add Problem for (int i = 0; i < numProblems; i ++){ char letter = 'A'; @@ -232,7 +233,7 @@ private void initData(IInternalContest contest, int numTeams, int numProblems) { contest.addLanguage(language); String[] judgementNames = { "Yes", "No - incorrect output", "No - compilation error", "Contact staff", "No - Security Violation" }; - + String[] acronyms = { "AC", "WA", "CE", "WA", "SV" }; for (int i = 0; i < judgementNames.length; i++) { @@ -243,14 +244,14 @@ private void initData(IInternalContest contest, int numTeams, int numProblems) { /** * Create a new run in the contest. - * + * * @param contest * @return created run. */ private Run getARun(IInternalContest contest) { Problem problem = contest.getProblems()[0]; Language language = contest.getLanguages()[0]; - + Account account = contest.getAccounts(ClientType.Type.TEAM).firstElement(); ClientId id = account.getClientId(); @@ -261,7 +262,7 @@ private Run getARun(IInternalContest contest) { /** * Create a new run in the contest. - * + * * @param contest * @param elapsedMinutes * @return created run. @@ -269,7 +270,7 @@ private Run getARun(IInternalContest contest) { private Run getARun(IInternalContest contest, int elapsedMinutes) { Problem problem = contest.getProblems()[0]; Language language = contest.getLanguages()[0]; - + Account account = contest.getAccounts(ClientType.Type.TEAM).firstElement(); ClientId id = account.getClientId(); @@ -280,14 +281,14 @@ private Run getARun(IInternalContest contest, int elapsedMinutes) { /** * Verify XML created for a single unjudged run. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException */ public void testOneRunUnjudged() throws IOException, ClassNotFoundException, FileSecurityException { InternalContest contest = new InternalContest(); - + initContestData(contest); Run run = getARun(contest); // Directory where test data is @@ -298,39 +299,39 @@ public void testOneRunUnjudged() throws IOException, ClassNotFoundException, Fil // } RunFiles runFiles = new RunFiles(run, loadData.getAbsolutePath()); - + contest.addRun(run, runFiles, null); - + checkOutputXML(contest); } /** * Verify XML created for a single unjudged run. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException - * @throws RunUnavailableException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException + * @throws RunUnavailableException */ public void testMixedjudged() throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { InternalContest contest = new InternalContest(); - + initContestData(contest); Run run = getARun(contest, 5); RunFiles runFiles = new RunFiles(run, loadData.getAbsolutePath()); - + contest.addRun(run, runFiles, null); createJudgedRun(contest, 0, false, 7); - + run = getARun(contest, 10); contest.addRun(run, runFiles, null); createJudgedRun(contest, 0, true, 15); - + checkOutputXML(contest); } - + public void testCESVNoJudgements() throws Exception { String [] runsData = { @@ -347,14 +348,14 @@ public void testCESVNoJudgements() throws Exception { "11,2,B,35,No,No,1", // zero -- not solved "12,2,B,40,No,No,1", // zero -- not solved }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team1,1,23", "2,team2,1,50" }; - + String [] rankData5 = { "1,team1,1,33", // +7 for SV + 3 for CE "2,team2,1,57" // +7 for SV @@ -364,63 +365,63 @@ public void testCESVNoJudgements() throws Exception { scoreboardTest(2, runsData, rankData); scoreboardTest(2, runsData, rankData5, alt5); - } - + } + /** * Create a judged run - * + * * @param contest * @param judgementIndex - the judgement list index * @param solved - was this run solved/Yes judgement - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException - * @throws RunUnavailableException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException + * @throws RunUnavailableException */ public void createJudgedRun (IInternalContest contest, int judgementIndex, boolean solved) throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { Run run = getARun(contest); RunFiles runFiles = new RunFiles(run, loadData.getAbsolutePath()); - + contest.addRun(run, runFiles, null); - + ClientId who = contest.getAccounts(ClientType.Type.JUDGE).firstElement().getClientId(); - + SampleContest.checkOutRun(contest, run, who); - + Judgement judgement = contest.getJudgements()[judgementIndex]; // Judge as No - + JudgementRecord judgementRecord = new JudgementRecord(judgement.getElementId(), who, solved, false); contest.addRunJudgement(run, judgementRecord, null, who); - + } /** * Submit and judge a run. - * + * * @param contest * @param judgementIndex * @param solved - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException - * @throws RunUnavailableException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException + * @throws RunUnavailableException */ public void createJudgedRun (IInternalContest contest, int judgementIndex, boolean solved, int elapsedMinutes) throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException{ Run run = getARun(contest, elapsedMinutes); RunFiles runFiles = new RunFiles(run, loadData.getAbsolutePath()); - + contest.addRun(run, runFiles, null); - + ClientId who = contest.getAccounts(ClientType.Type.JUDGE).firstElement().getClientId(); assertFalse ("Could not retrieve first judge ", who == null); - + SampleContest.checkOutRun(contest, run, who); - + Judgement judgement = contest.getJudgements()[judgementIndex]; // Judge as No - + JudgementRecord judgementRecord = new JudgementRecord(judgement.getElementId(), who, solved, false); contest.addRunJudgement(run, judgementRecord, null, who); - + } /** * Get XML from ScoringAlgorithm and test whether it can be parsed. @@ -435,20 +436,20 @@ public void checkOutputXML (IInternalContest contest) { * @param obeyFreeze */ public void checkOutputXML (IInternalContest contest, boolean obeyFreeze) { - + try { DefaultScoringAlgorithm defaultScoringAlgorithm = new DefaultScoringAlgorithm(); defaultScoringAlgorithm.setObeyFreeze(obeyFreeze); String xmlString = defaultScoringAlgorithm.getStandings(contest, new Properties(), log); - + // getStandings should always return a well-formed xml assertFalse("getStandings returned null ", xmlString == null); assertFalse("getStandings returned empty string ", xmlString.trim().length() == 0); - + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(new InputSource(new StringReader(xmlString))); - + // getStandings should always return a well-formed xml assertFalse("getStandings returned null ", xmlString == null); assertFalse("getStandings returned empty string ", xmlString.trim().length() == 0); @@ -466,36 +467,36 @@ public void checkOutputXML (IInternalContest contest, boolean obeyFreeze) { /** * Verify XML created for a single judged run. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException - * @throws RunUnavailableException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException + * @throws RunUnavailableException */ public void testOneRunJudged() throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { InternalContest contest = new InternalContest(); - + initContestData(contest); - + createJudgedRun(contest, 2, false); checkOutputXML(contest); - + } - + /** * Verify XML created for 5 judged runs, one solved, four no's. - * @throws FileSecurityException - * @throws ClassNotFoundException - * @throws IOException - * @throws RunUnavailableException + * @throws FileSecurityException + * @throws ClassNotFoundException + * @throws IOException + * @throws RunUnavailableException */ public void testFiveRunsJudged() throws IOException, ClassNotFoundException, FileSecurityException, RunUnavailableException { InternalContest contest = new InternalContest(); - + initContestData(contest); - + createJudgedRun(contest, 2, false); createJudgedRun(contest, 0, true); createJudgedRun(contest, 3, false); @@ -503,19 +504,19 @@ public void testFiveRunsJudged() throws IOException, ClassNotFoundException, Fil checkOutputXML(contest); } - - + + /** * CASE (1): "When Solved, all runs before Yes". - * + * * Created from testing/boardtest.html - * @throws Exception - * + * @throws Exception + * */ public void testScoreboardCaseOne () throws Exception { // RunID TeamID Prob Time Result - + String [] runsData = { "1,1,A,1,No", // 20 (a No before first yes) "2,1,A,3,Yes", // 3 (first yes counts Minute points but never Run Penalty points) @@ -531,21 +532,21 @@ public void testScoreboardCaseOne () throws Exception { "12,2,B,50,No", // zero -- not solved "13,2,B,55,No", // zero -- not solved }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team1,1,23", "2,team2,1,30" }; - + scoreboardTest (2, runsData, rankData); } - + public void testScoreboardCaseOneA() throws Exception{ - + // RunID TeamID Prob Time Result - + String [] runsData = { "2,8,C,1,No", "15,8,D,1,Yes", @@ -556,9 +557,9 @@ public void testScoreboardCaseOneA() throws Exception{ "52,8,C,1,Yes", "65,8,B,2,Yes", }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team8,4,45", "2,team1,0,0", @@ -569,45 +570,45 @@ public void testScoreboardCaseOneA() throws Exception{ "2,team6,0,0", "2,team7,0,0", }; - + scoreboardTest (8, runsData, rankData); } - - + + /** * Tests for cases where Yes is before No, and multiple yes at same elapsed time. - * + * * Both runs have same elapsed time, the tie breaker is runId. - * Also tests when one or more Yes are after first yes - * @throws Exception + * Also tests when one or more Yes are after first yes + * @throws Exception */ public void testNoBeforeYesSameElapsed() throws Exception{ - + // RunID TeamID Prob Time Result - + String [] runsData = { "15,8,D,12,Yes", "16,8,D,12,No", - + "24,4,B,15,Yes", "25,4,B,15,No", "26,4,B,15,No", - + "28,2,C,22,Yes", "29,2,C,22,No", "30,2,C,22,Yes", "30,2,C,22,Yes", }; - + /** - * - * - * + * + * + * */ - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team8,1,12", "2,team4,1,15", @@ -618,78 +619,78 @@ public void testNoBeforeYesSameElapsed() throws Exception{ "4,team6,0,0", "4,team7,0,0", }; - + scoreboardTest (8, runsData, rankData); } - + /** * CASE (2): "When Solved, all No Runs". * * Created from testing/boardtest.html - * @throws Exception + * @throws Exception */ public void testScoreboardCaseTwo () throws Exception{ - + // RunID TeamID Prob Time Result - + String [] runsData = { "1,1,A,1,No", // 20 "2,1,A,3,Yes", // 3 (Minute points for 1st Yes count) "3,1,A,5,No", // 20 (a No on a solved problem) "4,1,A,7,Yes", // zero (only "No's" count) "5,1,A,9,No", // 20 (another No on the solved problem) - + "6,1,B,11,No", // zero (problem has not been solved) "7,1,B,13,No", // zero (problem has not been solved) - + "8,2,A,30,Yes", // 30 (Minute points for 1st Yes) - + "9,2,B,35,No", // zero -- not solved "10,2,B,40,No", // zero -- not solved "11,2,B,45,No", // zero -- not solved "12,2,B,50,No", // zero -- not solved "13,2,B,55,No", // zero -- not solved }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team1,1,23", "2,team2,1,30", }; - + // TODO when SA supports count all runs, replace rankData - + /** * Case 2 tests when all no runs are counted, the current SA * does not support this scoring method. The commented * rankData is the proper results */ -// Team 2 -- 30 <-- Team 2 is now winning with a lower score +// Team 2 -- 30 <-- Team 2 is now winning with a lower score // Team 1 -- 63 (same database; different scoring method) - + // String [] rankData = { // "1,team2,1,30", // "2,team1,1,63", // }; - - + + scoreboardTest (2, runsData, rankData); - + } - + /** * CASE (3): "When Solved, All Runs" - * + * * Created from testing/boardtest.html - * @throws Exception + * @throws Exception */ public void testScoreboardCaseThree () throws Exception { - + // RunID TeamID Prob Time Result - + String [] runsData = { "1,1,A,1,No", // 20 @@ -709,53 +710,53 @@ public void testScoreboardCaseThree () throws Exception { "12,2,B,50,No", // zero -- not solved "13,2,B,55,No", // zero -- not solved }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team1,1,23", "2,team2,1,30" }; - + // TODO when SA supports count all runs, replace rankData - + /** * Case 3 tests when all runs are counted, the current SA * does not support this scoring method. The commented * rankData is the proper results */ - + // String [] rankData = { // "1,team2,1,30" // "2,team1,1,83", // }; - + scoreboardTest (2, runsData, rankData); } - + /** * CASE (4): "All Runs" - * + * * Created from testing/boardtest.html - * @throws Exception + * @throws Exception */ public void testScoreboardCaseFour () throws Exception { - + // RunID TeamID Prob Time Result - + String [] runsData = { "1,1,A,1,No", //20 "2,1,A,3,Yes", //3 (first yes counts Minutes only) "3,1,A,5,No", //20 - "4,1,A,7,Yes", //20 + "4,1,A,7,Yes", //20 "5,1,A,9,No", //20 - + "6,1,B,11,No", //20 (all runs count) "7,1,B,13,No", //20 (all runs count) - + "8,2,A,30,Yes", //30 - + "9,2,B,35,No", //20 (all runs count) "10,2,B,40,No", //20 (all runs count) "11,2,B,45,No", //20 (all runs count) @@ -763,37 +764,37 @@ public void testScoreboardCaseFour () throws Exception { "13,2,B,55,No", //20 (all runs count) }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team1,1,23", "2,team2,1,30" }; - + // TODO when SA supports count all runs, replace rankData - + /** * Case 4 tests when all runs are counted, the current SA * does not support this scoring method. The commented * rankData is the proper results */ - + // Team 1 -- 123 <-- Team 1 is winning again // Team 2 -- 130 - + // String [] rankData = { // "1,team1,1,123", // "2,team2,1,130" // }; - + scoreboardTest (2, runsData, rankData); } /** - * @throws Exception - * + * @throws Exception + * */ public void testScoreboard55 () throws Exception{ @@ -887,11 +888,11 @@ public void testScoreboard55 () throws Exception{ "87,17,C,2,No", "88,7,A,2,New", "89,20,B,2,No", - "90,12,C,2,No" + "90,12,C,2,No" }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team8,3,4", "2,team20,2,2", @@ -916,49 +917,49 @@ public void testScoreboard55 () throws Exception{ "13,team17,0,0", "13,team22,0,0", }; - + scoreboardTest (22, runsData, rankData); } - + /** * Test a No before a Yes, both runs same elapsed time. - * @throws Exception + * @throws Exception */ public void testNoYes () throws Exception{ // RunID TeamID Prob Time Result - + String [] runsData = { "5,2,A,12,No", "6,2,A,12,Yes", }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team2,1,32", "2,team1,0,0", }; - + scoreboardTest (2, runsData, rankData); } - - + + /** * Have run that has a BEING_JUDGED state and should show same standing * as the state were JUDGED. - * + * * Test for Bug 407 - The SA fails to reflect prelim judgements. - * + * * based on CASE (1): "When Solved, all runs before Yes". - * + * * Created from testing/boardtest.html - * @throws Exception - * + * @throws Exception + * */ public void testScoreboardForBeingJudgedState () throws Exception { // RunID TeamID Prob Time Result - + String [] runsData = { "1,1,A,1,No", // 20 (a No before first yes) "2,1,A,3,Yes", // 3 (first yes counts Minute points but never Run Penalty points) @@ -974,28 +975,28 @@ public void testScoreboardForBeingJudgedState () throws Exception { "12,2,B,50,No", // zero -- not solved "13,2,B,55,No", // zero -- not solved }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team1,1,23", "2,team2,1,30" }; - + // startExplorer(getOutputDataDirectory()); - + InternalContest contest = new InternalContest(); - + int numTeams = 2; initData(contest, numTeams, 5); - + for (String runInfoLine : runsData) { SampleContest.addRunFromInfo(contest, runInfoLine); } Run [] runs = contest.getRuns(); - + for (Run run : runs){ run.setStatus(RunStates.BEING_JUDGED); } @@ -1006,9 +1007,9 @@ public void testScoreboardForBeingJudgedState () throws Exception { } /** - * Test tie breaker down to last yes submission time. - * @throws Exception - * + * Test tie breaker down to last yes submission time. + * @throws Exception + * */ public void testTieBreakerSubmissionTime() throws Exception{ @@ -1018,30 +1019,30 @@ public void testTieBreakerSubmissionTime() throws Exception{ // Tertiary Sort = earliest submittal of last submission (low to high) // Forth Sort = teamName (low to high) // Fifth Sort = clientId (low to high) - + // RunID TeamID Prob Time Result - + String [] runsData = { "5,5,A,12,No", - "6,5,A,12,Yes", - + "6,5,A,12,Yes", + "7,6,A,12,No", "8,6,A,12,Yes", - + // Both solve 1 score 32 (no for 20, 12 min) "15,5,B,21,No", - "16,5,B,22,Yes", - + "16,5,B,22,Yes", + "25,6,B,21,No", - "26,6,B,22,Yes", - + "26,6,B,22,Yes", + // Both solve 2 score 42 (no for 20 and 22 min) // total 74 each }; - + // Rank TeamId Solved Penalty - + String [] rankData = { // must identical score, sort by team display name, identical ranks. "1,team5,2,74", @@ -1051,13 +1052,13 @@ public void testTieBreakerSubmissionTime() throws Exception{ "3,team3,0,0", "3,team4,0,0", }; - + scoreboardTest (6, runsData, rankData); } /** - * Test whether SA respects send to team - * @throws Exception + * Test whether SA respects send to team + * @throws Exception */ public void testSendToTeams() throws Exception{ @@ -1067,33 +1068,33 @@ public void testSendToTeams() throws Exception{ // Tertiary Sort = earliest submittal of last submission (low to high) // Forth Sort = teamName (low to high) // Fifth Sort = clientId (low to high) - + // RunID TeamID Prob Time Result - + String [] runsData = { "5,5,A,12,No", - "6,5,A,12,Yes", + "6,5,A,12,Yes", // t5 solves A, 32 pts = 20 + 12 - + "7,6,A,12,No,Yes", "8,6,A,12,Yes,No", - + // t6 does not solve, 0 solve, 0 pts "15,5,B,21,No", - "16,5,B,22,Yes,No", - + "16,5,B,22,Yes,No", + // t5 does solve B, but not sent to team/used 0 pts 0 solved - + "25,6,B,21,No,No", "26,6,B,22,Yes,Yes", - + // t6 solves B, 22 pts because No at 21 is NOT counted. }; - + // Rank TeamId Solved Penalty - + String [] rankData = { // rank, team, solved, pts "1,team6,1,22", @@ -1103,12 +1104,12 @@ public void testSendToTeams() throws Exception{ "3,team3,0,0", "3,team4,0,0", }; - + scoreboardTest (6, runsData, rankData, true); } public void testShortSchoolName() throws Exception { - + try { InternalContest contest = new InternalContest(); @@ -1126,11 +1127,11 @@ public void testShortSchoolName() throws Exception { DefaultScoringAlgorithm defaultScoringAlgorithm = new DefaultScoringAlgorithm(); defaultScoringAlgorithm.setObeyFreeze(false); String xmlString = defaultScoringAlgorithm.getStandings(contest, new Properties(), log); - + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(new InputSource(new StringReader(xmlString))); - + // getStandings should always return a well-formed xml NodeList byTagName = document.getElementsByTagName("teamStanding"); Node team1 = byTagName.item(0).getAttributes().getNamedItem("shortSchoolName"); @@ -1148,68 +1149,68 @@ public void testShortSchoolName() throws Exception { } public void testAltScoring() throws Exception { // RunID TeamID Prob Time Result - + String [] runsData = { "1,1,A,1,No", // 20 "2,1,A,3,Yes", // 3 (Minute points for 1st Yes count) "3,1,A,5,No", // 20 (a No on a solved problem) "4,1,A,7,Yes", // zero (only "No's" count) "5,1,A,9,No", // 20 (another No on the solved problem) - + "6,1,B,11,No", // zero (problem has not been solved) "7,1,B,13,No", // zero (problem has not been solved) - + "8,2,A,30,Yes", // 30 (Minute points for 1st Yes) - + "9,2,B,35,No", // zero -- not solved "10,2,B,40,No", // zero -- not solved "11,2,B,45,No", // zero -- not solved "12,2,B,50,No", // zero -- not solved "13,2,B,55,No", // zero -- not solved }; - + // Rank TeamId Solved Penalty - + // alt1: 0 0 200 - + String[] alt1rankData = { "1,team1,1,200", "2,team2,1,200", // tie-breaker causes rank 2 }; - + scoreboardTest (2, runsData, alt1rankData, alt1); // alt2: 30 5 0 String[] alt2rankData = { "1,team1,1,45", // 1 no@30 each + 3 min * 5 "2,team2,1,150", // 5*30 }; - + scoreboardTest (2, runsData, alt2rankData, alt2); - + // alt3: 0 10 0 String[] alt3rankData = { "1,team1,1,30", // 3 min * 10 "2,team2,1,300", // 30 min * 10 }; - + scoreboardTest (2, runsData, alt3rankData, alt3); - + // alt4: 5 0 20 String[] alt4rankData = { "1,team2,1,20", // base yes "2,team1,1,25", // base yes + 1 no }; - + scoreboardTest (2, runsData, alt4rankData, alt4); } /** * This is a test for bug 691 - * @throws Exception + * @throws Exception */ public void testDeletedProblem() throws Exception { - + InternalContest contest = new InternalContest(); int numTeams = 2; @@ -1219,14 +1220,14 @@ public void testDeletedProblem() throws Exception { "1,1,A,1,No", //20 "2,1,A,3,Yes", //3 (first yes counts Minutes only) "3,1,A,5,No", //20 - "4,1,A,7,Yes", //20 + "4,1,A,7,Yes", //20 "5,1,A,9,No", //20 - + "6,1,B,11,No", //20 (all runs count) "7,1,B,13,No", //20 (all runs count) - + "8,2,A,30,Yes", //30 - + "9,2,B,35,No", //20 (all runs count) "10,2,B,40,No", //20 (all runs count) "11,2,B,45,No", //20 (all runs count) @@ -1234,9 +1235,9 @@ public void testDeletedProblem() throws Exception { "13,2,B,55,No", //20 (all runs count) }; - + // Rank TeamId Solved Penalty - + String [] rankData = { "1,team1,0,0", "1,team2,0,0" @@ -1274,14 +1275,14 @@ public void testDeletedProblem() throws Exception { assertTrue("Error in XML output " + e.getMessage(), true); e.printStackTrace(); } - + // skip past nodes to find teamStanding node NodeList list = document.getDocumentElement().getChildNodes(); - + int rankIndex = 0; - + for(int i=0; i * rankdata is compared with XML from DefaultScoringAlgorithm * and if all ranks/fields match, passes the test. - * + * * @param contest * @param rankData */ @@ -1732,17 +1734,17 @@ private void confirmRanks(InternalContest contest, String[] rankData) { /** * Compares a standings Row with an expected row. - * + * * Each row contains: rank, name, number solved, points. - * + * * @param rankIndex * @param standingsRow * @param expectedRow */ private void compareRanking(int rankIndex, String[] standingsRow, String [] expectedRow) { - + // Object[] cols = { "Rank", "Name", "Solved", "Points" }; - + int idx = 0; assertEquals("Standings row "+rankIndex+" rank incorrect, ", expectedRow[idx], standingsRow[idx]); // assertTrue ("Standings row "+rankIndex+" rank wrong expected "+expectedRow[idx]+" found "+standingsRow[idx], standingsRow[idx].equals(expectedRow[idx])); @@ -1757,6 +1759,7 @@ private void compareRanking(int rankIndex, String[] standingsRow, String [] expe // assertTrue ("Standings row "+rankIndex+" points wrong expected "+expectedRow[idx]+" found "+standingsRow[idx], standingsRow[idx].equals(expectedRow[idx])); } + @Override protected void tearDown() throws Exception { super.tearDown(); } diff --git a/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTest.java b/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTest.java index 20726dccd..36558225e 100644 --- a/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTest.java +++ b/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTest.java @@ -42,7 +42,7 @@ import edu.csus.ecs.pc2.util.ScoreboardVariableReplacer; /** - * Unit tests. + * Unit tests. * @author pc2@ecs.csus.edu */ @@ -75,7 +75,7 @@ public void testBasic() throws Exception { } } - + public void testScoringAdjustments() throws Exception { SampleContest sampleContest = new SampleContest(); @@ -88,7 +88,7 @@ public void testScoringAdjustments() throws Exception { Account account = contest.getAccounts(ClientType.Type.TEAM).firstElement(); account.setScoringAdjustment(-2); contest.updateAccount(account); - + StandingsRecord[] standingsRecords = scoringAlgorithm.getStandingsRecords(contest, new Properties()); assertEquals("scoring adjustment -2", 10, standingsRecords[0].getPenaltyPoints()); account.setScoringAdjustment(-15); @@ -128,7 +128,7 @@ public void testHonorScoreboardFreezeUnfreeze() throws Exception { StandingsRecord standingsRecord = standingsRecords[1]; JsonNode rootNode = mapper.readTree(standingsRecord.toString()); for (Iterator iterator = rootNode.findValue("listOfSummaryInfo").elements(); iterator.hasNext();) { - JsonNode type = (JsonNode) iterator.next(); + JsonNode type = iterator.next(); long pendingRunCount = type.get("pendingRunCount").asLong(); long numberSubmitted = type.get("numberSubmitted").asLong(); assertEquals("for team 2 number submitted should equal pending run count", numberSubmitted, pendingRunCount); @@ -143,7 +143,7 @@ public void testHonorScoreboardFreezeUnfreeze() throws Exception { rootNode = mapper.readTree(standingsRecord.toString()); for (Iterator> iterator = rootNode.findValue("listOfSummaryInfo").fields(); iterator.hasNext();) { - Entry type = (Entry) iterator.next(); + Entry type = iterator.next(); if (type.getKey().equals("2")) { long pendingRunCount = type.getValue().get("pendingRunCount").asLong(); long numberSubmitted = type.getValue().get("numberSubmitted").asLong(); @@ -155,7 +155,7 @@ public void testHonorScoreboardFreezeUnfreeze() throws Exception { /** * Submit and judge a run. - * + * * @param contest * @param judgementIndex * @param solved @@ -187,7 +187,7 @@ public Run createJudgedRun(IInternalContest contest, int judgementIndex, boolean /** * Create a new run in the contest. - * + * * @param contest * @param elapsedMinutes * @return created run. @@ -237,7 +237,7 @@ public void testGroupWinners() throws Exception { if (debugMode){ System.out.println("debug records " + standingsRecords.length); } - + assertEquals("Expecting one team per group", teamsPerGroup * groupNames.length, standingsRecords.length); for (Group group : contest.getGroups()) { @@ -250,7 +250,7 @@ public void testGroupWinners() throws Exception { } // TODO CCS figure out assertNull fails below - + // Run aRun = createJudgedRun(contest, 0, true, 12); // aRun.setSubmitter(getTeamAccounts(contest)[0].getClientId()); // assign to team 1 // @@ -276,16 +276,16 @@ public void testGroupWinners() throws Exception { private void assignGroups(IInternalContest contest, Group group, int startr, int count) { Account[] accounts = getTeamAccounts(contest); for (int i = startr; i < startr + count; i++) { - accounts[i].setGroupId(group.getElementId()); + accounts[i].addGroupId(group.getElementId(), (i == startr)); if (debugMode) { - System.out.println("debug Account " + accounts[i].getClientId() + " " + accounts[i].getGroupId()); + System.out.println("debug Account " + accounts[i].getClientId() + " " + accounts[i].getGroupIds().toString()); } } } - - + + protected Run [] addTc1Runs(IInternalContest contest) throws Exception { - + String [] runsDataList = { // "1,101,B,1,Yes", // "2,151,C,1,Yes", // @@ -316,28 +316,28 @@ private void assignGroups(IInternalContest contest, Group group, int startr, int return contest.getRuns(); } - + private void initializeStaticLog(String name) { StaticLog.setLog(new Log("logs", name + ".log")); } - + public void testWithTestContest1() throws Exception { - + initializeStaticLog(getName()); InternalContest contest = new InternalContest(); String cdpDir = getTestSampleContestDirectory("tc1"); - + IContestLoader loader = new ContestSnakeYAMLLoader(); loader.initializeContest(contest, new File( cdpDir)); setFirstTeamClient(contest); - + Group[] groups = contest.getGroups(); - + for (Group group : groups) { String divName = ScoreboardUtilities.getDivision(group.getDisplayName()); assertNotNull("No division found for "+group.getDisplayName(), divName); } - + Account[] accounts = getTeamAccounts(contest); Arrays.sort(accounts, new AccountComparator()); for (Account account : accounts) { @@ -350,18 +350,18 @@ public void testWithTestContest1() throws Exception { assertEquals("Expecting # jugements", 10, judgements.length); addTc1Runs(contest); - + Run[] runlist = contest.getRuns(); for (Run run : runlist) { assertNotNull("Expecting account for "+run.getSubmitter(), contest.getAccount(run.getSubmitter())); String div = ScoreboardUtilities.getDivision(contest, run.getSubmitter()); assertNotNull("Missing division for "+run.getSubmitter(), div); } - + assertEquals("Expecting # runs", 21, runlist.length); - + ClientId client1 = accounts[5].getClientId(); - Group group = contest.getGroup(contest.getAccount(client1).getGroupId()); + Group group = contest.getGroup(contest.getAccount(client1).getPrimaryGroupId()); Run[] runs = ScoreboardUtilities.getRunsForUserDivision(client1, contest); assertEquals("Expecting runs matching group " + group, 7, runs.length); @@ -371,12 +371,12 @@ public void testWithTestContest1() throws Exception { assertEquals("Expecting runs matching group " + group, 5, runs.length); NewScoringAlgorithm scoringAlgorithm = new NewScoringAlgorithm(); - + Account acc = contest.getAccount(client1); - assertNotNull(acc.getGroupId()); - Group group2 = contest.getGroup(acc.getGroupId()); + assertNotNull(acc.getPrimaryGroupId()); + Group group2 = contest.getGroup(acc.getPrimaryGroupId()); assertNotNull(group2); - + String divString = ScoreboardUtilities.getDivision(contest, client1); Integer division = new Integer(divString); ClientId id = contest.getClientId(); @@ -384,7 +384,7 @@ public void testWithTestContest1() throws Exception { StandingsRecord[] standingsRecords = scoringAlgorithm.getStandingsRecords(contest, division,DefaultScoringAlgorithm.getDefaultProperties(), false, runs); assertEquals("Expecting standing records for client "+client1, 18, standingsRecords.length); - + ClientId lastClient = accounts[accounts.length-1].getClientId(); division = 3; standingsRecords = scoringAlgorithm.getStandingsRecords(contest, division, DefaultScoringAlgorithm.getDefaultProperties(), false, runs); @@ -393,30 +393,30 @@ public void testWithTestContest1() throws Exception { division = 1; Run[] divRuns = ScoreboardUtilities.getRunsForDivision(contest, division.toString()); assertEquals("Expecting run count for division "+division, 5, divRuns.length); - + division = 2; divRuns = ScoreboardUtilities.getRunsForDivision(contest, division.toString()); assertEquals("Expecting run count for division "+division, 7, divRuns.length); - + division = 3; divRuns = ScoreboardUtilities.getRunsForDivision(contest, division.toString()); assertEquals("Expecting run count for division "+division, 9, divRuns.length); - - + + } /** * Assign client for contest to first team. - * + * * @param contest */ private void setFirstTeamClient(IInternalContest contest) { Account[] acc = getTeamAccounts(contest); Arrays.sort(acc, new AccountComparator()); contest.setClientId(acc[0].getClientId()); - + } - + /** * Test whether NSA team name matches Team Display Format name * @throws Exception @@ -479,8 +479,8 @@ public void testTeamDisplayFormat() throws Exception { Account account = contest.getAccount(clientId); Group group = null; - if (account.getGroupId() != null) { - group = contest.getGroup(account.getGroupId()); + if (account.getPrimaryGroupId() != null) { + group = contest.getGroup(account.getPrimaryGroupId()); } String expectedDisplayName = ScoreboardVariableReplacer.substituteDisplayNameVariables(teamScoreboardDisplayForamtString, account, group); diff --git a/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTestLong.java b/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTestLong.java index 334a7bbbf..b0cbf9dde 100644 --- a/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTestLong.java +++ b/test/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithmTestLong.java @@ -1,4 +1,4 @@ -// 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.core.scoring; import java.io.IOException; @@ -45,7 +45,7 @@ /** * Unit tests. - * + * * @author pc2@ecs.csus.edu * @version $Id: NewScoringAlgorithmTestLong.java 161 2010-03-14 06:37:02Z laned $ */ @@ -77,6 +77,7 @@ public NewScoringAlgorithmTestLong(String name) { // alt4: 5 0 20 private Properties alt4 = populateProperties(5, 0, 20); + @Override protected void setUp() throws Exception { super.setUp(); @@ -129,9 +130,9 @@ public void testNoData() { /** * Initialize the contest. - * + * * Initialize with problems, languages, accounts, judgements. - * + * * @param contest */ private void initContestData(IInternalContest contest) { @@ -165,7 +166,7 @@ private void initContestData(IInternalContest contest) { /** * Create and return a new scoreboard client. - * + * * @param contest * @return a ClientId for newly created scoreboard account. */ @@ -176,7 +177,7 @@ private ClientId createBoardAccount(IInternalContest contest) { /** * Insure that there is one team and one judge in the contest model. - * + * * @param contest */ private void checkForJudgeAndTeam(IInternalContest contest) { @@ -192,7 +193,7 @@ private void checkForJudgeAndTeam(IInternalContest contest) { /** * Initialize contest with teams, problems, languages, judgements. - * + * * @param contest * @param numTeams * @param numProblems @@ -231,7 +232,7 @@ private void initData(IInternalContest contest, int numTeams, int numProblems) { /** * Create a new run in the contest. - * + * * @param contest * @return created run. */ @@ -249,7 +250,7 @@ private Run getARun(IInternalContest contest) { /** * Create a new run in the contest. - * + * * @param contest * @param elapsedMinutes * @return created run. @@ -268,7 +269,7 @@ private Run getARun(IInternalContest contest, int elapsedMinutes) { /** * Verify XML created for a single unjudged run. - * + * * @throws FileSecurityException * @throws ClassNotFoundException * @throws IOException @@ -288,7 +289,7 @@ public void testOneRunUnjudged() throws IOException, ClassNotFoundException, Fil /** * Verify XML created for a single unjudged run. - * + * * @throws FileSecurityException * @throws ClassNotFoundException * @throws IOException @@ -315,7 +316,7 @@ public void testMixedjudged() throws IOException, ClassNotFoundException, FileSe /** * Create a judged run - * + * * @param contest * @param judgementIndex * - the judgement list index @@ -344,7 +345,7 @@ public void createJudgedRun(IInternalContest contest, int judgementIndex, boolea /** * Submit and judge a run. - * + * * @param contest * @param judgementIndex * @param solved @@ -372,7 +373,7 @@ public void createJudgedRun(IInternalContest contest, int judgementIndex, boolea /** * Get XML from ScoringAlgorithm and test whether it can be parsed. - * + * * @param contest */ public void checkOutputXML(IInternalContest contest) { @@ -400,7 +401,7 @@ public void checkOutputXML(IInternalContest contest) { /** * Verify XML created for a single judged run. - * + * * @throws FileSecurityException * @throws ClassNotFoundException * @throws IOException @@ -418,7 +419,7 @@ public void testOneRunJudged() throws IOException, ClassNotFoundException, FileS /** * Verify XML created for 5 judged runs, one solved, four no's. - * + * * @throws FileSecurityException * @throws ClassNotFoundException * @throws IOException @@ -455,9 +456,9 @@ private void checkOutRun(IInternalContest contest, Run run, ClientId judgeId) { /** * CASE (1): "When Solved, all runs before Yes". - * + * * Created from testing/boardtest.html - * + * */ public void testScoreboardCaseOne() { // RunID TeamID Prob Time Result @@ -486,9 +487,9 @@ public void testScoreboardCaseOne() { /** * CASE (1): "When Solved, all runs before Yes" - test group ranks. - * + * * Created from testing/boardtest.html - * + * */ public void testScoreboardCaseOneGroupRanks() { // RunID TeamID Prob Time Result @@ -532,7 +533,7 @@ public void testScoreboardCaseOneA() { /** * Tests for cases where Yes is before No, and multiple yes at same elapsed time. - * + * * Both runs have same elapsed time, the tie breaker is runId. Also tests when one or more Yes are after first yes */ public void testNoBeforeYesSameElapsed() { @@ -550,9 +551,9 @@ public void testNoBeforeYesSameElapsed() { "30,2,C,22,Yes", }; /** - * - * - * + * + * + * */ // Rank TeamId Solved Penalty @@ -563,7 +564,7 @@ public void testNoBeforeYesSameElapsed() { /** * CASE (2): "When Solved, all No Runs". - * + * * Created from testing/boardtest.html */ public void testScoreboardCaseTwo() { @@ -610,7 +611,7 @@ public void testScoreboardCaseTwo() { /** * CASE (3): "When Solved, All Runs" - * + * * Created from testing/boardtest.html */ public void testScoreboardCaseThree() { @@ -656,7 +657,7 @@ public void testScoreboardCaseThree() { /** * CASE (4): "All Runs" - * + * * Created from testing/boardtest.html */ public void testScoreboardCaseFour() { @@ -704,7 +705,7 @@ public void testScoreboardCaseFour() { } /** - * + * */ public void testScoreboard55() { @@ -750,7 +751,7 @@ public void testScoreboard55() { } /** - * + * */ public void testScoreboard55Groups() { @@ -836,13 +837,13 @@ public void testNoYes() { /** * Have run that has a BEING_JUDGED state and should show same standing as the state were JUDGED. - * + * * Test for Bug 407 - The SA fails to reflect prelim judgements. - * + * * based on CASE (1): "When Solved, all runs before Yes". - * + * * Created from testing/boardtest.html - * + * */ public void testScoreboardForBeingJudgedState() { // RunID TeamID Prob Time Result @@ -894,7 +895,7 @@ public void testScoreboardForBeingJudgedState() { /** * Test tie breaker down to last yes submission time. - * + * */ public void testTieBreakerSubmissionTime() { @@ -1038,7 +1039,8 @@ private void scoreboardTest(int numTeams, String[] runsDataList, String[] rankDa ClientId clientId = new ClientId(contest.getSiteNumber(), Type.TEAM, teamNumber); Account account = contest.getAccount(clientId); - account.setGroupId(group.getElementId()); + account.clearGroups(); + account.addGroupId(group.getElementId(), true); } } @@ -1127,7 +1129,7 @@ private void confirmRanks(InternalContest contest, String[] rankData, Properties int rankIndex = 0; for (int i = 0; i < list.getLength(); i++) { - Node node = (Node) list.item(i); + Node node = list.item(i); String name = node.getNodeName(); if (name.equals("teamStanding")) { String[] standingsRow = fetchStanding(node, compareGroupRanks); @@ -1259,9 +1261,9 @@ public void testZZZZEOCSettings() { /** * Test the SA given a list of runs and outcomes. - * + * * rankDataList array is array of string, thus: Rank TeamDisplayName Solved Penalty, for example: "1,team5,2,74", - * + * * @param numTeams * @param runsDataList * @param rankDataList @@ -1295,9 +1297,9 @@ public void scoreboardTest(int numTeams, String[] runsDataList, String[] rankDat /** * add run to list of runs in a contest. - * + * * Files found in runInfoLine, comma delmited - * + * *
          * 0 - run id, int
          * 1 - team id, int
    @@ -1305,13 +1307,13 @@ public void scoreboardTest(int numTeams, String[] runsDataList, String[] rankDat
          * 3 - elapsed, int
          * 4 - solved, String "Yes" or No
          * 5 - send to teams, Yes or No
    -     * 
    +     *
          * Example:
          * "6,5,A,12,Yes"
          * "6,5,A,12,Yes,Yes"
    -     * 
    +     *
          * 
    - * + * * @param contest * @param runInfoLine * @throws FileSecurityException @@ -1400,7 +1402,7 @@ private void addTheRun(InternalContest contest, String runInfoLine) { /** * Fetch string from nodes. - * + * * @param node * @param fetchGroupRank * if true, fetch the group rank @@ -1444,11 +1446,11 @@ private String[] fetchStanding(Node node, boolean fetchGroupRank) { /** * Confirms ranks between runs in contest and rankData. - * + * * rankdata is a array of string, each string contains comma delimited fields: rank,teamid,solved,points *

    * rankdata is compared with XML from DefaultScoringAlgorithm and if all ranks/fields match, passes the test. - * + * * @param contest * @param rankData * @param compareGroupRanks @@ -1459,9 +1461,9 @@ private void confirmRanks(InternalContest contest, String[] rankData) { /** * Confirms ranks between runs in contest and rankData. - * + * * @see #confirmGroupRanks(InternalContest, String[]) - * + * * @param contest * @param rankData */ @@ -1471,9 +1473,9 @@ private void confirmGroupRanks(InternalContest contest, String[] rankData) { /** * Compares a standings Row with an expected row. - * + * * Each row contains: rank, name, number solved, points. - * + * * @param rankIndex * @param standingsRow * @param expectedRow @@ -1519,9 +1521,9 @@ protected void tearDown() throws Exception { /** * Test getRuns method. - * + * * Test whether runs from getRuns(runs,clientId,problem) method returns proper number of runs. - * + * * @throws Exception */ public void testgetRuns() throws Exception { @@ -1604,9 +1606,9 @@ public void testgetRuns() throws Exception { /** * Test get methods in ProblemSummary. - * + * * Bug 1028. - * + * * @throws Exception */ public void testRankings1028() throws Exception { @@ -1655,7 +1657,7 @@ public void testRankings1028() throws Exception { /** * Representation of a StandingRecord - * + * * name, rank, solved, lastsolved, points; then for each problem: problem#, numRuns, points, judgedCount, pendingCount ; */ String[] rep = { // @@ -1697,7 +1699,7 @@ public void testRankings1028() throws Exception { /** * Prints test data representation for standingsRecord. - * + * * @param standingsRecord */ protected void dumpStandingsRecordSampleData(StandingsRecord standingsRecord) { @@ -1710,10 +1712,10 @@ protected void dumpStandingsRecordSampleData(StandingsRecord standingsRecord) { /** * Returns sample/test data. - * + * * name, rank, solved, lastsolvedtime, penalty then
    * probbNum, points, judgecount, pendingcount - * + * * @param standingsRecord * @return */ diff --git a/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013Test.java b/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013Test.java index 174a2f2eb..829f7975f 100644 --- a/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013Test.java +++ b/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXML2013Test.java @@ -1,4 +1,4 @@ -// 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.exports.ccs; import java.io.File; @@ -47,7 +47,7 @@ import edu.csus.ecs.pc2.core.util.XMLMemento; /** - * Unit Test. + * Unit Test. * * @author pc2@ecs.csus.edu * @version $Id$ @@ -63,9 +63,9 @@ public class EventFeedXML2013Test extends AbstractTestCase { private final boolean debugMode = false; private IInternalContest contest = null; - + private SampleContest sample = new SampleContest(); - + public EventFeedXML2013Test(String testName) { super(testName); } @@ -76,9 +76,9 @@ protected void setUp() throws Exception { int siteNumber = 1; contest = sample.createContest(siteNumber, 1, 22, 12, true); - + addContestInfo (contest, "Contest Title"); - + Group group1 = new Group("Mississippi"); group1.setGroupId(1024); contest.addGroup(group1); @@ -95,20 +95,20 @@ protected void setUp() throws Exception { /** * Add random runs */ - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); - + /** * Add Run Judgements. */ addRunJudgements(contest, runs); - + } - + private void addRunJudgements (IInternalContest inContest, Run[] runs) throws Exception { addRunJudgements(inContest, runs, 0); } - + /** * Add run judgements, test cases and run files. * @param inContest @@ -119,7 +119,7 @@ private void addRunJudgements (IInternalContest inContest, Run[] runs) throws Ex private void addRunJudgements (IInternalContest inContest, Run[] runs, int numberOfTestCases) throws Exception { ClientId judgeId = inContest.getAccounts(Type.JUDGE).firstElement().getClientId(); - + Judgement judgement; String sampleFileName = sample.getSampleFile(); @@ -149,7 +149,7 @@ private void addContestInfo(IInternalContest contest2, String title) { /** * Assign group to team startIdx to endIdx. - * + * * @param group * @param startIdx * @param endIdx @@ -157,7 +157,8 @@ private void addContestInfo(IInternalContest contest2, String title) { private void assignTeamGroup(Group group, int startIdx, int endIdx) { Account[] teams = getTeamAccounts(); for (int i = startIdx; i < endIdx; i++) { - teams[i].setGroupId(group.getElementId()); + teams[i].clearGroups(); + teams[i].addGroupId(group.getElementId(), true); } } @@ -167,7 +168,7 @@ private void assignTeamGroup(Group group, int startIdx, int endIdx) { */ private Account[] getTeamAccounts() { Vector teams = contest.getAccounts(Type.TEAM); - Account[] accounts = (Account[]) teams.toArray(new Account[teams.size()]); + Account[] accounts = teams.toArray(new Account[teams.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; } @@ -177,10 +178,10 @@ public void testContestElement() throws Exception { EventFeedXML2013 eventFeedXML = new EventFeedXML2013(); InternalContest internalContest = new InternalContest(); - + ContestInformation info = new ContestInformation(); info.setContestTitle("Title One"); - + /** * Check info tag, if there is no contest data in InternalContest. */ @@ -193,7 +194,7 @@ public void testContestElement() throws Exception { System.out.println(xml); System.out.println(); } - + /** * Check complete EventFeed XML, if there is no contest data in InternalContest. */ @@ -208,21 +209,21 @@ public void testContestElement() throws Exception { assertXMLCounts(xml, EventFeedXML2013.CONTEST_TAG, 1); assertXMLCounts(xml, EventFeedXML2013.INFO_TAG, 1); - + } /** * Test <info> tag. - * + * * @throws Exception */ public void testInfoElement() throws Exception { EventFeedXML2013 eventFeedXML = new EventFeedXML2013(); - + ContestInformation info = new ContestInformation(); info.setContestTitle("Title One"); - + String xml = toContestXML(eventFeedXML.createInfoElement(contest, info)); if (debugMode){ @@ -238,7 +239,7 @@ public void testInfoElement() throws Exception { if (debugMode){ System.out.println(xml); } - + testForValidXML (xml); } @@ -262,7 +263,7 @@ public void testLanguageElement() throws Exception { /** * Create Contest XML. - * + * * @param memento * @return */ @@ -312,13 +313,13 @@ public void testJudgementElement() throws Exception { sequence ++; } } - + @SuppressWarnings("unused") private String toString(BalloonDeliveryInfo info) { return "Notification: "+info.getKey()+" "+info.getTimeSent(); } - + public void testProblemElement() throws Exception { @@ -376,14 +377,14 @@ public void testClarElement() throws Exception { EventFeedXML2013 eventFeedXML = new EventFeedXML2013(); Clarification[] clarifications = contest.getClarifications(); Arrays.sort(clarifications, new ClarificationComparator()); - + Problem problem = contest.getProblems()[0]; ClientId who = getTeamAccounts()[0].getClientId(); String question = "What is the meaning of pi ?"; - + Clarification clar = new Clarification(who,problem,question); contest.addClarification(clar); - + clarifications = contest.getClarifications(); assertEquals("Expecting number of clarifications ",1,clarifications.length); @@ -396,7 +397,7 @@ public void testClarElement() throws Exception { assertXMLCounts(xml, "clar>", 0); // not expecting assertXMLCounts(xml, EventFeedXML2013.CLARIFICATION_TAG, 1); } - + } public void testRunElement() throws Exception { @@ -421,13 +422,13 @@ public void testRunElement() throws Exception { public void testFinalizedElement() throws Exception { EventFeedXML2013 eventFeedXML = new EventFeedXML2013(); - + FinalizeData data = new FinalizeData(); data.setGoldRank(8); data.setBronzeRank(20); data.setSilverRank(16); data.setComment("Finalized by the Ultimiate Finalizer role"); - + String xml = CONTEST_START_TAG + eventFeedXML.createFinalizeXML(contest, data); if (debugMode){ System.out.println(" -- testFinalizedElement "); @@ -435,9 +436,9 @@ public void testFinalizedElement() throws Exception { } testForValidXML (xml); - + assertXMLCounts(xml, "finalized", 1); - + } public void testStartupElement() throws Exception { @@ -451,7 +452,7 @@ public void testStartupElement() throws Exception { } xml = xml + CONTEST_END_TAG; testForValidXML (xml); - + assertXMLCounts(xml, EventFeedXML2013.CONTEST_TAG, 1); assertXMLCounts(xml, EventFeedXML2013.INFO_TAG, 1); assertXMLCounts(xml, EventFeedXML2013.JUDGEMENT_TAG, 9); @@ -459,10 +460,10 @@ public void testStartupElement() throws Exception { } - + /** * Print counts in xml string for EventFeed elements. - * + * * @param comment * @param xmlString * @throws ParserConfigurationException @@ -476,13 +477,13 @@ public void printElemntCounts(String comment, String xmlString) throws ParserCon System.out.println("printElemntCounts " + comment); String[] tagnames = getAllEventFeedTagNames(); - + Arrays.sort(tagnames); for (String tagName : tagnames) { System.out.println(tagName + " count = " + getTagCount(xmlString, tagName)); } } - + public String [] getAllEventFeedTagNames (){ String[] tagnames = { // EventFeedXML2013.CONTEST_TAG, EventFeedXML2013.INFO_TAG, EventFeedXML2013.REGION_TAG, EventFeedXML2013.PROBLEM_TAG, EventFeedXML2013.LANGUAGE_TAG, EventFeedXML2013.TEAM_TAG, @@ -503,7 +504,7 @@ public void testToXML() throws Exception { } testForValidXML (xml); } - + protected void startEventFeed(int port) throws IOException { ServerSocket server = new ServerSocket(port); @@ -534,34 +535,34 @@ protected void startEventFeed(int port) throws IOException { } public void testTestCase() throws Exception { - + EventFeedXML2013 eventFeedXML = new EventFeedXML2013(); - + int siteNumber = 2; - + IInternalContest testCaseContest = sample.createContest(siteNumber, 1, 22, 12, true); - + /** * Add random runs */ - + Run[] runs = sample.createRandomRuns(testCaseContest, 12, true, true, true); - + for (Run run : runs) { testCaseContest.acceptRun(run, new RunFiles(run,getSamplesSourceFilename(SUMIT_SOURCE_FILENAME))); } - + createDataFilesForContest (testCaseContest, 5); runs = testCaseContest.getRuns(); - + assertEquals("number of runs ", 12, runs.length); - + sample.assignSampleGroups(testCaseContest, "Group Thing One", "Group Thing Two"); - + sample.assignTeamExternalIds(testCaseContest, 424242); - - + + /** * Add Run Judgements. */ @@ -570,19 +571,19 @@ public void testTestCase() throws Exception { assertEquals("test cases ", 60, countTestCases(testCaseContest)); String xml = eventFeedXML.toXML(testCaseContest); - + if (debugMode){ System.out.println(" -- testTestCase "); System.out.println(xml); } - + testForValidXML(xml); assertEquals ("No empty external-id tags expected", 0, countString(xml, "external-id/")); - + validateUsingSchema (xml); assertEquals ("No empty OCS values expected", 0, countString(xml, "OCS")); - + assertXMLCounts(xml, EventFeedXML2013.CONTEST_TAG, 1); assertXMLCounts(xml, EventFeedXML2013.INFO_TAG, 1); assertXMLCounts(xml, EventFeedXML2013.JUDGEMENT_TAG, 9); @@ -591,26 +592,26 @@ public void testTestCase() throws Exception { assertXMLCounts(xml, EventFeedXML2013.REGION_TAG, 24); assertXMLCounts(xml, EventFeedXML2013.RUN_TAG, 12); assertXMLCounts(xml, EventFeedXML2013.TEAM_TAG, 34); // both teams and team tag in submissions - assertXMLCounts(xml, EventFeedXML2013.TESTCASE_TAG, 12 * 5); + assertXMLCounts(xml, EventFeedXML2013.TESTCASE_TAG, 12 * 5); /** * Test FINALIZE */ - + FinalizeData data = new FinalizeData(); data.setCertified(true); data.setComment(getName()+" test"); testCaseContest.setFinalizeData(data); - + xml = eventFeedXML.toXML(testCaseContest); - + testForValidXML (xml); - + assertXMLCounts(xml, EventFeedXML2013.FINALIZE_TAG, 1); assertXMLCounts(xml, "comment", 1); - + testCaseContest = sample.createContest(siteNumber, 1, 22, 12, true); - + assertNotNull(testCaseContest.getClientId()); assertNotNull(testCaseContest.getAccounts(Type.JUDGE).firstElement().getClientId()); @@ -627,7 +628,7 @@ public void testTestCase() throws Exception { } int numruns = 5; - + for (int i = runs.length - numruns; i < runs.length; i++) { runs[i].setElapsedMins(300); // / set all runs to elapsed time 100 RunTestCase[] testCases = runs[i].getRunTestCases(); @@ -635,7 +636,7 @@ public void testTestCase() throws Exception { runTestCaseResult.setElapsedMS(300 * Constants.MS_PER_MINUTE); } } - + assertEquals(5,runs.length); Filter filter = new Filter(); @@ -646,13 +647,13 @@ public void testTestCase() throws Exception { assertXMLCounts(xml, EventFeedXML2013.TESTCASE_TAG, numruns * 5); } - + private int countTestCases(IInternalContest testCaseContest) { int num = 0; - + Run[] runs = testCaseContest.getRuns(); for (Run run : runs) { - + RunTestCase[] testCases = run.getRunTestCases(); for (RunTestCase runTestCaseResult : testCases) { if (runTestCaseResult.matchesJudgement(run.getJudgementRecord())){ @@ -666,24 +667,24 @@ private int countTestCases(IInternalContest testCaseContest) { private void validateUsingSchema(String xml) throws Exception { // TODO 623 TODO CCS get this schema validation to work. - + // String prolog = ""; // String newXML = prolog + xml; // String [] contents = {newXML}; // String filename ="testing.ef.xml"; // writeFileContents(filename, contents); // System.out.println("Wrote xml to file "+filename); - + String schemaFileName = getSchemaFilename("event-feed-2013.xsd"); assertFileExists(schemaFileName); - + // testForValidXML (newXML, schemaFileName); - + } /** * A very simple - * + * * @param xml * @param string * @param i @@ -693,12 +694,12 @@ private void validateUsingSchema(String xml) throws Exception { private void assertXMLCounts(String xmlString, String string, int count) throws Exception { assertEquals("Expecting occurances (for " + string + ")", count, getTagCount(xmlString, string)); } - + /** * Finds name in xml string, compares node/element values against expectedValue. * @param xmlString * @param name XML tag name - * @param expectedValue + * @param expectedValue * @throws ParserConfigurationException * @throws SAXException * @throws IOException @@ -710,20 +711,20 @@ private void assertXMLNodeValueEquals (String xmlString, String name, String exp System.out.println("xml = "+xmlString); fail("Expecting to find nodes for name "+name); } - + // System.out.println("Nodes length = "+nodes.getLength()); // for (int i = 0; i < nodes.getLength(); i++) { // Node node = nodes.item(i); // System.out.println("Looking for "+name+", found node "+node.getNodeName()+" "+node.getNodeValue()+" child is: "+node.getChildNodes().item(0).getNodeValue()); // } - + String childValue = nodes.item(0).getChildNodes().item(0).getNodeValue(); if (! expectedValue.equals(childValue)){ System.out.println("xml = "+xmlString); assertEquals("Expecting value for "+name, expectedValue, childValue); } } - + private int getTagCount(String xmlString, String string) throws ParserConfigurationException, SAXException, IOException { Document document = getDocument(xmlString); @@ -733,7 +734,7 @@ private int getTagCount(String xmlString, String string) throws ParserConfigurat /** * Creates a single sample testcase (data and answer file) for each problem. - * + * * @param inContest * @throws FileNotFoundException */ @@ -779,23 +780,23 @@ private void createDataFilesForContest(IInternalContest inContest, int numberTes } } } - - + + public void testIsYounger() throws Exception { - + String [] runsData = { "1,1,A,1,No", //20 "2,1,A,3,Yes", //3 (first yes counts Minutes only) "3,1,A,5,No", //20 - "4,1,A,7,Yes", //20 + "4,1,A,7,Yes", //20 "5,1,A,9,No", //20 - + "6,1,B,11,No", //20 (all runs count) "7,1,B,13,No", //20 (all runs count) - + "8,2,A,30,Yes", //30 - + "9,2,B,35,No", //20 (all runs count) "10,2,B,40,No", //20 (all runs count) "11,2,B,45,No", //20 (all runs count) @@ -808,11 +809,11 @@ public void testIsYounger() throws Exception { "16,2,A,330,Yes", // doesn't count, yes after yes }; - + IInternalContest testContest = sample.createStandardContest(); - + for (String runInfoLine : runsData) { - sample.addARun(testContest, runInfoLine); + sample.addARun(testContest, runInfoLine); } Run[] runs = testContest.getRuns(); @@ -830,11 +831,11 @@ public void testIsYounger() throws Exception { Run laterRun = feed.getFirstSolvedRun(testContest, runs[15].getSubmitter(), runs[15].getProblemId()); Run earliest = runs[7]; assertEquals("Expecting first solved run to be " + earliest, earliest, laterRun); - + } - + // SOMEDAY: Ensure that teams that are not shown on scoreboard runs are not in feed. - + public void testDeletedRuns() throws Exception { // TODO: ensure that EventFeedXMLTest includes this method. @@ -902,25 +903,25 @@ public void testDeletedRuns() throws Exception { private Account getAdminAccount(IInternalContest inContest) { return inContest.getAccounts(Type.ADMINISTRATOR).firstElement(); } - + public void testExternalId() throws Exception { - + EventFeedXML2013 eventFeedXML = new EventFeedXML2013(); int siteNumber = 2; IInternalContest testCaseContest = sample.createContest(siteNumber, 1, 22, 12, true); - + String xmlString = eventFeedXML.toXML(testCaseContest); - + testForValidXML(xmlString); Document document = getDocument(xmlString); - + NodeList nodes = document.getElementsByTagName("external-id"); - + assertEquals("external id count ", 22,nodes.getLength()); - + for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); String value = node.getTextContent(); @@ -932,35 +933,35 @@ public void testExternalId() throws Exception { } assertXMLCounts(xmlString, EventFeedXML2013.TEAM_TAG, 22); - + } - + public void testUnjudgedRuns() throws Exception { - + EventFeedXML2013 eventFeedXML = new EventFeedXML2013(); int siteNumber = 2; IInternalContest testCaseContest = sample.createContest(siteNumber, 1, 22, 12, true); - + Account acc = SampleContest.getTeamAccounts(testCaseContest)[0]; ClientId clientId = acc.getClientId(); Problem problem = testCaseContest.getProblems()[0]; Run run = sample.createRun(testCaseContest, clientId, problem); - + testCaseContest.addRun(run); String xmlString = eventFeedXML.toXML(testCaseContest); - + testForValidXML(xmlString); - + } - - + + /** * Create socket server on port. - * + * * @param args */ public static void main(String[] args) { diff --git a/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXMLTest.java b/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXMLTest.java index b870818c4..1b4108190 100644 --- a/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXMLTest.java +++ b/test/edu/csus/ecs/pc2/exports/ccs/EventFeedXMLTest.java @@ -52,7 +52,7 @@ /** * Test Event Feed XML. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -67,9 +67,9 @@ public class EventFeedXMLTest extends AbstractTestCase { private final boolean debugMode = false; private IInternalContest contest = null; - + private SampleContest sample = new SampleContest(); - + private NotificationUtilities notificationUtilities = new NotificationUtilities(); private boolean exitingServer = false; @@ -80,9 +80,9 @@ protected void setUp() throws Exception { int siteNumber = 1; contest = sample.createContest(siteNumber, 1, 22, 12, true); - + addContestInfo (contest, "Contest Title"); - + Group group1 = new Group("Mississippi"); group1.setGroupId(1024); contest.addGroup(group1); @@ -99,20 +99,20 @@ protected void setUp() throws Exception { /** * Add random runs */ - + Run[] runs = sample.createRandomRuns(contest, 12, true, true, true); - + /** * Add Run Judgements. */ addRunJudgements(contest, runs); - + } - + private void addRunJudgements (IInternalContest inContest, Run[] runs) throws Exception { addRunJudgements(inContest, runs, 0); } - + private void addRunJudgements (IInternalContest inContest, Run[] runs, int numberOfTestCases) throws Exception { ClientId judgeId = inContest.getAccounts(Type.JUDGE).firstElement().getClientId(); @@ -143,7 +143,7 @@ private void addContestInfo(IInternalContest contest2, String title) { /** * Assign group to team startIdx to endIdx. - * + * * @param group * @param startIdx * @param endIdx @@ -151,7 +151,8 @@ private void addContestInfo(IInternalContest contest2, String title) { private void assignTeamGroup(Group group, int startIdx, int endIdx) { Account[] teams = getTeamAccounts(contest); for (int i = startIdx; i < endIdx; i++) { - teams[i].setGroupId(group.getElementId()); + teams[i].clearGroups(); + teams[i].addGroupId(group.getElementId(), true); } } @@ -161,10 +162,10 @@ public void testContestElement() throws Exception { EventFeedXML eventFeedXML = new EventFeedXML(); InternalContest internalContest = new InternalContest(); - + ContestInformation info = new ContestInformation(); info.setContestTitle("Title One"); - + /** * Check info tag, if there is no contest data in InternalContest. */ @@ -177,7 +178,7 @@ public void testContestElement() throws Exception { System.out.println(xml); System.out.println(); } - + /** * Check complete EventFeed XML, if there is no contest data in InternalContest. */ @@ -192,21 +193,21 @@ public void testContestElement() throws Exception { assertXMLCounts(xml, EventFeedXML.CONTEST_TAG, 1); assertXMLCounts(xml, EventFeedXML.INFO_TAG, 1); - + } /** * Test tag. - * + * * @throws Exception */ public void testInfoElement() throws Exception { EventFeedXML eventFeedXML = new EventFeedXML(); - + ContestInformation info = new ContestInformation(); info.setContestTitle("Title One"); - + String xml = toContestXML(eventFeedXML.createInfoElement(contest, info)); if (debugMode){ @@ -222,7 +223,7 @@ public void testInfoElement() throws Exception { if (debugMode){ System.out.println(xml); } - + testForValidXML (xml); } @@ -246,7 +247,7 @@ public void testLanguageElement() throws Exception { /** * Create Contest XML. - * + * * @param memento * @return */ @@ -296,13 +297,13 @@ public void testJudgementElement() throws Exception { testForValidXML (xml); } } - + public void testCreateBalloonElement() throws Exception { if (debugMode){ System.out.println(" -- testCreateBalloonElement "); } - + EventFeedXML eventFeedXML = new EventFeedXML(); for (Problem problem : contest.getProblems()) { String xml = toContestXML(eventFeedXML.createBalloonElement(contest, problem)); @@ -313,19 +314,19 @@ public void testCreateBalloonElement() throws Exception { testForValidXML (xml); } } - + private Run [] getSortedRuns() { Run[] runs = contest.getRuns(); Arrays.sort(runs, new RunComparator()); return runs; } - + public void testNotification() throws Exception { // Create notifications addNotifications(contest); - + /** * Solved runs is used to only count one solved run per team and problem. */ @@ -345,35 +346,35 @@ public void testNotification() throws Exception { BalloonDeliveryInfo[] deliveries = notificationUtilities.getBalloonDeliveries(contest); Arrays.sort(deliveries, new BalloonDeliveryComparator(contest)); int notificationSequenceNumber = 1; - + solved = solvedRuns.keySet().size(); - + assertEquals("Expected notifications for all solved", solved, deliveries.length); - + for (BalloonDeliveryInfo balloonDeliveryInfo : deliveries) { String xml = toContestXML(evenFeedXML.createElement(contest, balloonDeliveryInfo, notificationSequenceNumber)); - + if (debugMode){ System.out.println(xml); } testForValidXML (xml); notificationSequenceNumber++; } - + assertEquals("Expected notifification for all solved.", notificationSequenceNumber-1, deliveries.length); } - + /** * For all solved runs insure that each has a notification. - * + * * @param contest2 * @return */ private int addNotifications(IInternalContest contest2) { - + int count = 0; - + for (Run run : getSortedRuns()) { if (run.isSolved()) { @@ -383,17 +384,17 @@ private int addNotifications(IInternalContest contest2) { } } } - + return count; } - - + + /** * Create a notification if needed. - * + * * Checks for existing notification, only creates * a notification if no notification exists. - * + * * @param contest2 * @param run */ @@ -415,7 +416,7 @@ private String toString(BalloonDeliveryInfo info) { return "Notification: "+info.getKey()+" "+info.getTimeSent(); } - + public void testProblemElement() throws Exception { @@ -513,13 +514,13 @@ public void testTestcaseElement() throws Exception { public void testFinalizedElement() throws Exception { EventFeedXML eventFeedXML = new EventFeedXML(); - + FinalizeData data = new FinalizeData(); data.setGoldRank(8); data.setBronzeRank(20); data.setSilverRank(16); data.setComment("Finalized by the Ultimiate Finalizer role"); - + String xml = eventFeedXML.createFinalizeXML(contest, data); if (debugMode){ System.out.println(" -- testFinalizedElement "); @@ -540,7 +541,7 @@ public void testStartupElement() throws Exception { } xml = xml + CONTEST_END_TAG; testForValidXML (xml); - + assertXMLCounts(xml, EventFeedXML.CONTEST_TAG, 1); assertXMLCounts(xml, EventFeedXML.INFO_TAG, 1); assertXMLCounts(xml, EventFeedXML.JUDGEMENT_TAG, 9); @@ -548,10 +549,10 @@ public void testStartupElement() throws Exception { } - + /** * Print counts in xml string for EventFeed elements. - * + * * @param comment * @param xmlString * @throws ParserConfigurationException @@ -565,13 +566,13 @@ public void printElemntCounts(String comment, String xmlString) throws ParserCon System.out.println("printElemntCounts " + comment); String[] tagnames = getAllEventFeedTagNames(); - + Arrays.sort(tagnames); for (String tagName : tagnames) { System.out.println(tagName + " count = " + getTagCount(xmlString, tagName)); } } - + public String [] getAllEventFeedTagNames (){ String[] tagnames = { // EventFeedXML.CONTEST_TAG, EventFeedXML.INFO_TAG, EventFeedXML.REGION_TAG, EventFeedXML.PROBLEM_TAG, EventFeedXML.LANGUAGE_TAG, EventFeedXML.TEAM_TAG, EventFeedXML.CLARIFICATION_TAG, @@ -592,7 +593,7 @@ public void testToXML() throws Exception { } testForValidXML (xml); } - + protected void startEventFeed(int port) throws IOException { ServerSocket server = new ServerSocket(port); @@ -620,41 +621,41 @@ protected void startEventFeed(int port) throws IOException { server.close(); } } - + server = null; } public void testTestCase() throws Exception { - + EventFeedXML eventFeedXML = new EventFeedXML(); - + int siteNumber = 2; - + IInternalContest testCaseContest = sample.createContest(siteNumber, 1, 22, 12, true); - + /** * Add random runs */ - + Run[] runs = sample.createRandomRuns(testCaseContest, 12, true, true, true); - + createDataFilesForContest (testCaseContest); - + /** * Add Run Judgements. */ addRunJudgements(testCaseContest, runs, 5); - + int expectedTeamTags = getTeamAccounts(testCaseContest).length + testCaseContest.getRuns().length; - + String xml = eventFeedXML.toXML(testCaseContest); if (debugMode){ System.out.println(" -- testTestCase "); System.out.println(xml); } testForValidXML (xml); - + assertXMLCounts(xml, EventFeedXML.CONTEST_TAG, 1); assertXMLCounts(xml, EventFeedXML.INFO_TAG, 1); assertXMLCounts(xml, EventFeedXML.JUDGEMENT_TAG, 9); @@ -664,31 +665,31 @@ public void testTestCase() throws Exception { assertXMLCounts(xml, EventFeedXML.REGION_TAG, 22); assertXMLCounts(xml, EventFeedXML.RUN_TAG, 12); assertXMLCounts(xml, EventFeedXML.TEAM_TAG, expectedTeamTags); - assertXMLCounts(xml, EventFeedXML.TESTCASE_TAG, 12 * 5); + assertXMLCounts(xml, EventFeedXML.TESTCASE_TAG, 12 * 5); /** * Test FINALIZE */ - + FinalizeData data = new FinalizeData(); data.setCertified(true); data.setComment(getName()+" test"); testCaseContest.setFinalizeData(data); - + xml = eventFeedXML.toXML(testCaseContest); - + testForValidXML (xml); - + assertXMLCounts(xml, EventFeedXML.FINALIZE_TAG, 1); assertXMLCounts(xml, "comment", 1); - + for (Run run : runs) { run.setElapsedMins(100); /// set all runs to elapsed time 100 } - + int numruns = 5; - + for (int i = runs.length - numruns; i < runs.length; i++) { runs[i].setElapsedMins(300); /// set all runs to elapsed time 100 RunTestCase[] testCases = runs[i].getRunTestCases(); @@ -696,52 +697,52 @@ public void testTestCase() throws Exception { runTestCaseResult.setElapsedMS(300 * Constants.MS_PER_MINUTE); } } - + Filter filter = new Filter(); filter.setStartElapsedTime(200); xml = eventFeedXML.toXML(testCaseContest, filter); - + assertXMLCounts(xml, EventFeedXML.RUN_TAG, numruns); - assertXMLCounts(xml, EventFeedXML.TESTCASE_TAG, numruns * 5); + assertXMLCounts(xml, EventFeedXML.TESTCASE_TAG, numruns * 5); } /** * Test to ensure that DISPLAY_ON_SCOREBOARD teams are not present in the event feed. - * + * * @throws Exception */ public void testTeamsNotOnScoreboard() throws Exception { - + EventFeedXML eventFeedXML = new EventFeedXML(); - + int siteNumber = 2; - + IInternalContest testCaseContest = sample.createContest(siteNumber, 1, 22, 12, true); - + /** * Add random runs */ - + Run[] runs = sample.createRandomRuns(testCaseContest, 12, true, true, true); - + createDataFilesForContest (testCaseContest); - + /** * Add Run Judgements. */ addRunJudgements(testCaseContest, runs, 5); - + int expectedTeamTags = getTeamAccounts(testCaseContest).length + testCaseContest.getRuns().length; String xml = eventFeedXML.toXML(testCaseContest); - + if (debugMode){ System.out.println(" -- testTestCase "); System.out.println(xml); } testForValidXML (xml); - + assertXMLCounts(xml, EventFeedXML.CONTEST_TAG, 1); assertXMLCounts(xml, EventFeedXML.INFO_TAG, 1); assertXMLCounts(xml, EventFeedXML.JUDGEMENT_TAG, 9); @@ -752,19 +753,19 @@ public void testTeamsNotOnScoreboard() throws Exception { assertXMLCounts(xml, EventFeedXML.RUN_TAG, 12); assertXMLCounts(xml, EventFeedXML.TEAM_TAG, expectedTeamTags); assertXMLCounts(xml, EventFeedXML.TESTCASE_TAG, 12 * 5); - + Vector vector = testCaseContest.getAccounts(Type.TEAM); - Account[] accounts = (Account[]) vector.toArray(new Account[vector.size()]); + Account[] accounts = vector.toArray(new Account[vector.size()]); Arrays.sort(accounts,new AccountComparator()); - + int dontShowCount = 7; - + assertEquals("Expecting number of runs ", 12, testCaseContest.getRuns().length); - + /** * Team 16 - Team 22 should not show on event feed. */ - + for (int i = 0; i < dontShowCount; i++) { Account account = accounts[accounts.length - 1 - i]; account.removePermission(edu.csus.ecs.pc2.core.security.Permission.Type.DISPLAY_ON_SCOREBOARD); @@ -773,18 +774,18 @@ public void testTeamsNotOnScoreboard() throws Exception { assertTrue("Expecting one run to be created", runs.length == 1); testCaseContest.addRun(runs[0]); } - + int numberTeamsToDisplay = getNumberOfTeamsTodisplay(testCaseContest); - + int numberOfRunsToDisplay = getNumberOfRunsToDisplay (testCaseContest); - + assertEquals ("Expeced teams to be displayed/included", getTeamAccounts(testCaseContest).length - dontShowCount, numberTeamsToDisplay); - + xml = eventFeedXML.toXML(testCaseContest); assertXMLCounts(xml, EventFeedXML.TEAM_TAG, numberTeamsToDisplay + numberOfRunsToDisplay); assertXMLCounts(xml, EventFeedXML.RUN_TAG, numberOfRunsToDisplay); } - + private int getNumberOfRunsToDisplay(IInternalContest testCaseContest) { Run[] runs = testCaseContest.getRuns(); int count = 0; @@ -800,7 +801,7 @@ private boolean teamDisplayedOnScoreboard(IInternalContest inContest, ClientId c return inContest.isAllowed(clientId, Permission.Type.DISPLAY_ON_SCOREBOARD); } - + private int getNumberOfTeamsTodisplay(IInternalContest testCaseContest) { Account[] accounts = getTeamAccounts(testCaseContest); int count = 0; @@ -820,7 +821,7 @@ public void viewString(String xml) throws IOException { /** * A very simple - * + * * @param xml * @param string * @param i @@ -830,12 +831,12 @@ public void viewString(String xml) throws IOException { private void assertXMLCounts(String xmlString, String string, int count) throws Exception { assertEquals("Expecting occurances (for" + string + ")", count, getTagCount(xmlString, string)); } - + /** * Finds name in xml string, compares node/element values against expectedValue. * @param xmlString * @param name XML tag name - * @param expectedValue + * @param expectedValue * @throws ParserConfigurationException * @throws SAXException * @throws IOException @@ -847,20 +848,20 @@ private void assertXMLNodeValueEquals (String xmlString, String name, String exp System.out.println("xml = "+xmlString); fail("Expecting to find nodes for name "+name); } - + // System.out.println("Nodes length = "+nodes.getLength()); // for (int i = 0; i < nodes.getLength(); i++) { // Node node = nodes.item(i); // System.out.println("Looking for "+name+", found node "+node.getNodeName()+" "+node.getNodeValue()+" child is: "+node.getChildNodes().item(0).getNodeValue()); // } - + String childValue = nodes.item(0).getChildNodes().item(0).getNodeValue(); if (! expectedValue.equals(childValue)){ System.out.println("xml = "+xmlString); assertEquals("Expecting value for "+name, expectedValue, childValue); } } - + private int getTagCount(String xmlString, String string) throws ParserConfigurationException, SAXException, IOException { Document document = getDocument(xmlString); @@ -870,7 +871,7 @@ private int getTagCount(String xmlString, String string) throws ParserConfigurat /** * Creates a single sample testcase (data and answer file) for each problem. - * + * * @param inContest * @throws FileNotFoundException */ @@ -913,8 +914,8 @@ private void createDataFilesForContest(IInternalContest inContest) throws FileNo } } } - - + + public void testgetColorSettings() throws Exception { EventFeedXML eventFeedXML = new EventFeedXML(); @@ -931,36 +932,36 @@ public void testgetColorSettings() throws Exception { assertNotNull("Expecting RGB for " + problem, colorRGB); // System.out.println(problem.getShortName()+"TAB"+color+" ATAB"+colorRGB); } - + eventFeedXML = new EventFeedXML(); aContest = new InternalContest(); - + for (Problem problem : problems) { aContest.addProblem(problem); } - + settings = eventFeedXML.getColorSettings(aContest); - + assertNull("Not expecting balloon settings ", settings); - + ensureDirectory(getDataDirectory()); - + String colorFile = getTestFilename("colors.txt"); // editFile(colorFile); assertFileExists(colorFile); - + eventFeedXML.setColorsFilename(colorFile); settings = eventFeedXML.getColorSettings(aContest); - + assertNotNull("Expecting color settings from "+colorFile, settings); - + String [] data = { // "sumit;Alice Blue A;F0F8FF", // "quadrangles;Antique White A;FAEBD7", // "finnigans;Beige A;F5F5DC", // }; - + for (Problem problem : problems) { String color = settings.getColor(problem); @@ -978,17 +979,17 @@ public void testgetColorSettings() throws Exception { } } } - + // File schemaFile = new File(getSchemaFilename(CCS_EVENT_FEED_SCHEMA_2013)); // String xmlString = eventFeedXML.toXML(aContest); - + // xmlString = "" + xmlString; // xmlString = ""; - + // editTempFile(xmlString); - + // System.out.println("xml="+xmlString); - + // try { // testForValidXML(xmlString, schemaFile); // } catch (Exception e) { @@ -998,20 +999,20 @@ public void testgetColorSettings() throws Exception { } public void testIsYounger() throws Exception { - + String [] runsData = { "1,1,A,1,No", //20 "2,1,A,3,Yes", //3 (first yes counts Minutes only) "3,1,A,5,No", //20 - "4,1,A,7,Yes", //20 + "4,1,A,7,Yes", //20 "5,1,A,9,No", //20 - + "6,1,B,11,No", //20 (all runs count) "7,1,B,13,No", //20 (all runs count) - + "8,2,A,30,Yes", //30 - + "9,2,B,35,No", //20 (all runs count) "10,2,B,40,No", //20 (all runs count) "11,2,B,45,No", //20 (all runs count) @@ -1024,11 +1025,11 @@ public void testIsYounger() throws Exception { "16,2,A,330,Yes", // doesn't count, yes after yes }; - + IInternalContest testContest = sample.createStandardContest(); - + for (String runInfoLine : runsData) { - sample.addARun(testContest, runInfoLine); + sample.addARun(testContest, runInfoLine); } Run[] runs = testContest.getRuns(); @@ -1046,12 +1047,12 @@ public void testIsYounger() throws Exception { Run laterRun = feed.getFirstSolvedRun(testContest, runs[15].getSubmitter(), runs[15].getProblemId()); Run earliest = runs[7]; assertEquals("Expecting first solved run to be " + earliest, earliest, laterRun); - + } /** * Create socket server on port. - * + * * @param args */ public static void main(String[] args) { @@ -1065,7 +1066,7 @@ public static void main(String[] args) { } } - + public void exitServer(){ this.exitingServer = true; } diff --git a/test/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXMLTest.java b/test/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXMLTest.java index 61a514063..023951391 100644 --- a/test/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXMLTest.java +++ b/test/edu/csus/ecs/pc2/exports/ccs/ResolverEventFeedXMLTest.java @@ -1,4 +1,4 @@ -// 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.exports.ccs; import java.io.File; @@ -52,7 +52,7 @@ /** * Unit Test. - * + * * @author Douglas A. Lane, PC^2 team pc2@ecs.csus.edu */ public class ResolverEventFeedXMLTest extends AbstractTestCase { @@ -138,7 +138,7 @@ private void addContestInfo(IInternalContest contest2, String title) { /** * Assign group to team startIdx to endIdx. - * + * * @param group * @param startIdx * @param endIdx @@ -146,18 +146,19 @@ private void addContestInfo(IInternalContest contest2, String title) { private void assignTeamGroup(Group group, int startIdx, int endIdx) { Account[] teams = getTeamAccounts(); for (int i = startIdx; i < endIdx; i++) { - teams[i].setGroupId(group.getElementId()); + teams[i].clearGroups(); + teams[i].addGroupId(group.getElementId(), true); } } /** * Return list of accounts sorted by team id. - * + * * @return */ private Account[] getTeamAccounts() { Vector teams = contest.getAccounts(Type.TEAM); - Account[] accounts = (Account[]) teams.toArray(new Account[teams.size()]); + Account[] accounts = teams.toArray(new Account[teams.size()]); Arrays.sort(accounts, new AccountComparator()); return accounts; } @@ -199,7 +200,7 @@ public void testContestElement() throws Exception { /** * Test <info> tag. - * + * * @throws Exception */ public void testInfoElement() throws Exception { @@ -225,7 +226,7 @@ public void testInfoElement() throws Exception { debugPrintln(xml); testForValidXML(xml); - + // System.out.println (xml); // assertXMLNodeValueEquals(xml, ", expectedValue); } @@ -246,7 +247,7 @@ public void testLanguageElement() throws Exception { /** * Create Contest XML. - * + * * @param memento * @return */ @@ -412,16 +413,16 @@ public void testStartupElement() throws Exception { assertXMLCounts(xml, ResolverEventFeedXML.INFO_TAG, 1); assertXMLCounts(xml, ResolverEventFeedXML.JUDGEMENT_TAG, 9); assertXMLCounts(xml, ResolverEventFeedXML.REGION_TAG, 24); - + assertFirstValueFound(xml,"starttime", "undefined"); - + // System.out.println("xml = "+xml); } - + /** * Print counts in xml string for EventFeed elements. - * + * * @param comment * @param xmlString * @throws ParserConfigurationException @@ -494,7 +495,7 @@ public void testTestCase() throws Exception { final int NUM_RUNS = 12 ; final int NUM_TESTCASES_PER_RUN = 5 ; - + ResolverEventFeedXML eventFeedXML = new ResolverEventFeedXML(); int siteNumber = 2; @@ -583,7 +584,7 @@ public void testTestCase() throws Exception { private void validateUsingSchema(String xml) throws Exception { // startExplorer(getSchemaDirectory()); - + String testDir = getOutputDataDirectory(this.getName()); ensureDirectory(testDir); @@ -594,7 +595,7 @@ private void validateUsingSchema(String xml) throws Exception { String newXML = prolog + xml; String[] contents = { newXML }; - + String outfilename = testDir + File.separator + "testing.ef.xml"; writeFileContents(outfilename, contents); @@ -611,14 +612,14 @@ private void validateUsingSchema(String xml) throws Exception { /** * Counts number of XML elements found in XML String - * + * * @param xmlString * @param elementName * @param count * @throws Exception */ private void assertXMLCounts(String xmlString, String elementName, int count) throws Exception { - + int actualCount = getTagCount(xmlString, elementName); // System.out.println("debug assertXMLCounts expecting "+count+" for "+elementName+" actual is "+actualCount); assertEquals("Expecting occurances (for" + elementName + ")", count, actualCount); @@ -626,7 +627,7 @@ private void assertXMLCounts(String xmlString, String elementName, int count) th /** * Finds name in xml string, compares node/element values against expectedValue. - * + * * @param xmlString * @param name * XML tag name @@ -655,36 +656,36 @@ private void assertXMLNodeValueEquals(String xmlString, String name, String expe assertEquals("Expecting value for " + name, expectedValue, childValue); } } - + /** * Test all info elements in XML. - * + * * Bug 1080 - * + * * @throws Exception */ public void testAllInfoElements() throws Exception { - + final int NUM_RUNS = 12; final int NUM_TESTCASES_PER_RUN = 5; - + ResolverEventFeedXML eventFeedXML = new ResolverEventFeedXML(); - + IInternalContest standardContest = new SampleContest().createStandardContest(); - + Run[] runs = sample.createRandomRuns(standardContest, NUM_RUNS, true, true, true); addRunJudgements(standardContest, runs, NUM_TESTCASES_PER_RUN); String xml = eventFeedXML.toXML(standardContest); - + // System.out.println(xml); testForValidXML(xml); - + // String f = "/tmp/stuf33.xml"; // printXMLToFile(f, xml); // editFile(f); - + assertXMLCounts(xml, ResolverEventFeedXML.CONTEST_TAG, 1); assertXMLCounts(xml, ResolverEventFeedXML.INFO_TAG, 1); assertXMLCounts(xml, ResolverEventFeedXML.JUDGEMENT_TAG, NUM_RUNS*NUM_TESTCASES_PER_RUN + 9); //9 judgement defs + 1 judgement per test case per run @@ -694,7 +695,7 @@ public void testAllInfoElements() throws Exception { assertXMLCounts(xml, ResolverEventFeedXML.RUN_TAG, 12); assertXMLCounts(xml, ResolverEventFeedXML.TEAM_TAG, 132); // both teams and team tag in submissions assertXMLCounts(xml, ResolverEventFeedXML.TESTCASE_TAG, NUM_RUNS*NUM_TESTCASES_PER_RUN); - + // // 5:00:00 @@ -705,9 +706,9 @@ public void testAllInfoElements() throws Exception { // default.--7840082699249977062 // 01:00:00 // - + String contestId = standardContest.getContestIdentifier().toLowerCase(); - + assertXMLNodeValueEquals(xml, "length", "5:00:00"); assertXMLNodeValueEquals(xml, "started", "False"); assertXMLNodeValueEquals(xml, "starttime", "undefined"); @@ -717,21 +718,21 @@ public void testAllInfoElements() throws Exception { assertXMLNodeValueEquals(xml, "scoreboard-freeze-length", "01:00:00"); assertXMLNodeValueEquals(xml, "starttime", "undefined"); - + ContestInformation info = standardContest.getContestInformation(); - + // define starttime, change freeze time - + GregorianCalendar newScheduledStartTime = new GregorianCalendar(); - + newScheduledStartTime.add(Calendar.HOUR_OF_DAY, 1); info.setScheduledStartTime(newScheduledStartTime); info.setFreezeTime("04:00:00"); standardContest.updateContestInformation(info); - + xml = eventFeedXML.toXML(standardContest); String startTime = XMLUtilities.formatSeconds(info.getScheduledStartDate().getTime()); - + assertXMLNodeValueEquals(xml, "scoreboard-freeze-length", "04:00:00"); assertXMLNodeValueEquals(xml, "starttime", startTime); } @@ -740,7 +741,7 @@ public void testAllInfoElements() throws Exception { * Write string to file. * @param filename * @param string - * @throws FileNotFoundException + * @throws FileNotFoundException */ public void printXMLToFile(String filename, String string) throws FileNotFoundException { String[] lines = { string }; @@ -748,9 +749,9 @@ public void printXMLToFile(String filename, String string) throws FileNotFoundEx } public void testScheduledStartTime() throws Exception { - + } - + private int getTagCount(String xmlString, String string) throws ParserConfigurationException, SAXException, IOException { Document document = getDocument(xmlString); @@ -760,7 +761,7 @@ private int getTagCount(String xmlString, String string) throws ParserConfigurat /** * Creates a single sample testcase (data and answer file) for each problem. - * + * * @param inContest * @throws FileNotFoundException */ @@ -975,7 +976,7 @@ public void testUnjudgedRuns() throws Exception { /** * Create socket server on port. - * + * * @param args */ public static void main(String[] args) { @@ -989,17 +990,17 @@ public static void main(String[] args) { } } - + /** * Test for started and running values. - * + * * Bug 1118. - * + * * @throws Exception */ public void testInfoStartedRunning() throws Exception { - + ResolverEventFeedXML eventFeedXML = new ResolverEventFeedXML(); IInternalContest standardContest = new SampleContest().createStandardContest(); @@ -1007,38 +1008,38 @@ public void testInfoStartedRunning() throws Exception { String xml = eventFeedXML.toXML(standardContest); testForValidXML(xml); - + assertXMLCounts(xml, ResolverEventFeedXML.CONTEST_TAG, 1); assertXMLCounts(xml, ResolverEventFeedXML.INFO_TAG, 1); assertXMLCounts(xml, "started", 1); assertXMLCounts(xml, "running", 1); - + assertXMLNodeValueEquals(xml, "length", "5:00:00"); assertXMLNodeValueEquals(xml, "started", "False"); assertXMLNodeValueEquals(xml, "running", "False"); assertXMLNodeValueEquals(xml, "starttime", "undefined"); - + assertFalse(standardContest.getContestTime().isContestRunning()); - + standardContest.startContest(standardContest.getSiteNumber()); - + xml = eventFeedXML.toXML(standardContest); testForValidXML(xml); - + assertTrue(standardContest.getContestTime().isContestRunning()); - + assertXMLNodeValueEquals(xml, "length", "5:00:00"); assertXMLNodeValueEquals(xml, "started", "True"); assertXMLNodeValueEquals(xml, "running", "True"); - + standardContest.stopContest(standardContest.getSiteNumber()); - + xml = eventFeedXML.toXML(standardContest); testForValidXML(xml); - + assertFalse(standardContest.getContestTime().isContestRunning()); - + assertXMLNodeValueEquals(xml, "length", "5:00:00"); assertXMLNodeValueEquals(xml, "started", "True"); assertXMLNodeValueEquals(xml, "running", "False"); diff --git a/test/edu/csus/ecs/pc2/services/web/EventFeedFilterTest.java b/test/edu/csus/ecs/pc2/services/web/EventFeedFilterTest.java index aa46af198..60aaa6385 100644 --- a/test/edu/csus/ecs/pc2/services/web/EventFeedFilterTest.java +++ b/test/edu/csus/ecs/pc2/services/web/EventFeedFilterTest.java @@ -1,4 +1,4 @@ -// 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.services.web; import edu.csus.ecs.pc2.core.exception.IllegalContestState; @@ -10,7 +10,7 @@ /** * Unit Test. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class EventFeedFilterTest extends AbstractTestCase { @@ -19,7 +19,7 @@ public void testNoFilter() throws Exception { EventFeedFilter filter = new EventFeedFilter(); - assertEquals("startid = , event types = ", filter.toString()); + assertEquals("startid = , event types = , groupids = ", filter.toString()); String[] lines = getStandardContestJSON(); assertEquals("Expected line count ", 143, lines.length); @@ -71,10 +71,10 @@ public void testgetEventFeedType() throws Exception { } - public void testgetEventFeedEequence() throws Exception { + public void testgetEventFeedSequence() throws Exception { EventFeedFilter filter = new EventFeedFilter(); String string = "{\"event\":\"judgement-types\", \"id\":\"pc2-9\", \"op\":\"create\", \"data\": {\"id\":\"WA3\", \"name\":\"How did you get into this place ?\", \"penalty\":true, \"solved\":false}}"; - assertEquals("pc2-9", filter.getEventFeedEequence(string)); + assertEquals("pc2-9", filter.getEventFeedSequence(string)); } } diff --git a/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java b/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java index 51d3c62b7..04eeeb130 100644 --- a/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java +++ b/test/edu/csus/ecs/pc2/util/ScoreboardVariableReplacerTest.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2021 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.util; import java.util.Arrays; @@ -11,7 +11,7 @@ /** * Unit tests. - * + * * @author Douglas A. Lane * */ @@ -19,7 +19,7 @@ public class ScoreboardVariableReplacerTest extends AbstractTestCase { /** * Test substituteDisplayNameVariables with var string, account and group. - * + * * @throws Exception */ public void testSubstituteDisplayNameVariablesStringAccountGroup() throws Exception { @@ -34,7 +34,7 @@ public void testSubstituteDisplayNameVariablesStringAccountGroup() throws Except Arrays.sort(accounts, new AccountComparator()); Account account3 = accounts[2]; - Group group = contest.getGroup(account3.getGroupId()); + Group group = contest.getGroup(account3.getPrimaryGroupId()); String inputString = "Team Name: " + ScoreboardVariableReplacer.TEAM_NAME + // ", number" + ScoreboardVariableReplacer.CLIENT_NUMBER + // @@ -54,7 +54,7 @@ public void testSubstituteDisplayNameVariablesStringAccountGroup() throws Except /** * Print all display variables and their values - * + * * @param contest * @param account */ @@ -70,7 +70,7 @@ public void printAllVariables(IInternalContest contest, Account account) { /** * Test substituteDisplayNameVariables with contest and account. - * + * * @throws Exception */ public void testSubstituteDisplayNameVariablesStringIInternalContestAccount() throws Exception { @@ -104,8 +104,8 @@ public void testSubstituteDisplayNameVariablesStringIInternalContestAccount() th assertEquals("Expecting sub string ", expected, actual); /** - * - * + * + * * GROUP_ID, // SHORT_SCHOOL_NAME, // LONG_SCHOOL_NAME, // COUNTRY_CODE, // */ diff --git a/testdata/LoadICPCTSVDataTest/teams.tsv b/testdata/LoadICPCTSVDataTest/teams.tsv index cdee24929..680c3f8e6 100644 --- a/testdata/LoadICPCTSVDataTest/teams.tsv +++ b/testdata/LoadICPCTSVDataTest/teams.tsv @@ -3,5 +3,5 @@ # teams 1 1 3232 905 Hoos University of Virginia U Virginia USA -null 3234 905 Rams University of Colorado U Colorado USA +null 3234 202,204,503 Rams University of Colorado U Colorado USA null 3236 206 Natural Log University of Chile U Chile CL