Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I1027 Add Contest Clock To WTI #1034

Open
wants to merge 142 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
7198a88
i1027: added contest timers to app-header files in "shared" module.
Dec 15, 2024
28836d8
i1027: added [email protected] to package.json
Dec 15, 2024
7df2b21
i1027: added various attempts to get HTML properly displaying clocks.
Dec 17, 2024
198fe1c
i1027: added setInterval object to gen timer ticks; added date func.
Dec 17, 2024
0d612ab
i1027: added attempts to access new ElapsedTimePipe from core module.
Dec 17, 2024
ac3ea6c
i1027: added attempts to access ElapsedTimePipe from shared module.
Dec 17, 2024
36ce45f
i1027: added new ElapsedTimePipe for formatting dates into dd:hh:mm:ss.
Dec 17, 2024
39786ef
i1027: copy 'constant.ts' into i1027 branch from PR871 branch.
clevengr Dec 18, 2024
9ac819e
i1027: added method getContestClock() to ContestServices (to both...
clevengr Dec 18, 2024
2335da0
i1027: added ContestClockModel to hold the WTI-API rep of ContestTime.
clevengr Dec 18, 2024
d189a1c
i1027: added support for getting contest start time via the PC2 API.
clevengr Dec 18, 2024
912fb68
i1027: add support for URL /contestclock to WTI-API server.
clevengr Dec 18, 2024
9705890
i1027: fix minor typo in javadoc comment.
clevengr Dec 18, 2024
6e386d8
i1027: add elapsedTimePipe to core/services -- an Angular "Pipe" that...
clevengr Dec 18, 2024
443238f
i1027: clean up app-header HTML to display elapsed time in two ways:
clevengr Dec 18, 2024
95b5e73
i1027: minor doc (comments) added.
clevengr Dec 21, 2024
6ed9b97
i1027: added newly-required getContestClock() method to mock service.
clevengr Dec 21, 2024
2356765
i1027: added fetch of PC2 contest clock upon successful login.
clevengr Dec 21, 2024
d55fa7f
i1027: removed no-longer-needed debugging; reformat code for readability
clevengr Dec 21, 2024
5111f9c
i1027: rename time display pipe, rework to accept numSeconds
clevengr Dec 22, 2024
41051a5
i1027: add WTI-UI model for contest clock.
clevengr Dec 24, 2024
a93102a
i1027: added Timer class to enable/disable incr of display times.
clevengr Dec 24, 2024
ea7490a
i1027: remove no-longer-used ngx-countdown.
clevengr Dec 25, 2024
1645b45
i1027: include title-setting in all routes, matching PR871.
clevengr Dec 25, 2024
7a71e77
i1027: update for compatibility with PR for i871.
clevengr Dec 26, 2024
732b89c
i1027: SharedModule: remove ngx-countdown; add ContestServices.
clevengr Dec 26, 2024
b4a84cc
i1027: rename contestClock Subject to contestClockEvent; add debugging
clevengr Dec 26, 2024
b06f0cb
i1027: make some console debug output conditional on DEBUG_MODE
clevengr Dec 26, 2024
17053d1
i1027: LoginPage: move ContestClock to ContestService; add doc and debug
clevengr Dec 26, 2024
5291696
i1027: IWebSocketService: make root-injectable; add debug output.
clevengr Dec 26, 2024
a089508
i1027: IContestService: make root-injectable; also:
clevengr Dec 26, 2024
d07f77c
i1027: DisplayTimePipe: added debug output.
clevengr Dec 26, 2024
0e82291
i1027: ContestTimerService: added error output when remainingTime<0
clevengr Dec 26, 2024
ab9b09a
i1027: ContestService: make root-injectable; add ContestTimer; also:
clevengr Dec 26, 2024
b6125c4
i1027: ContestMockService: add mock support for updating contest clock
clevengr Dec 26, 2024
c622bdd
i1027: AppComponent: add support matching i871-F5-in-WTI.
clevengr Dec 26, 2024
5fc8c51
i1027: moved timer support out of AppHeader (into ContestService); also:
clevengr Dec 26, 2024
23da9ff
i1027: ContestTimerService: added conditional debugging output.
clevengr Dec 27, 2024
d067987
i1027: ContestService: added conditional debugging output.
clevengr Dec 27, 2024
c6fb9b1
i1027: ContestService: return elapsed/remainging from Timer.
clevengr Dec 27, 2024
95ef304
i1027: LoginPageComponent: added conditional debugging output.
clevengr Dec 27, 2024
8793db2
i1027: AppHeaderComponent: added conditional debugging output.
clevengr Dec 27, 2024
0c13393
i1027: ContestClock: added additional documentation.
clevengr Dec 27, 2024
865767b
i1027: commented-out no-longer-needed debug output.
clevengr Dec 27, 2024
0a316c8
i1027: updated debug comments; added "todo".
clevengr Dec 27, 2024
bc5018b
i1027: refactor updateContestClock() to be a ContestService method.
clevengr Dec 28, 2024
d43bfce
i1027: IWebsocketService: invoke updateContestClock() on 'clock' msg.
clevengr Dec 28, 2024
f172644
i1027: LoginPageComponent: add comments (no code changes).
clevengr Dec 28, 2024
85529fc
i127: AppHeaderComponent: remove debugging display of raw seconds.
clevengr Dec 28, 2024
4cee5bd
i1027: WTI-API.ContestController: fix copy/paste typo in response.
clevengr Dec 28, 2024
01f2fa3
i1027: ContestTimer: set default remaining time to zero. Note that...
clevengr Dec 28, 2024
8913a05
i1027: added comments (no code changes).
clevengr Jan 1, 2025
9071684
i1027: added toString() method. This is important because...
clevengr Jan 1, 2025
6cc9d40
i1027: handle wallClockStartTime when contest hasn't been started; also,
clevengr Jan 2, 2025
458548c
i1027: update contest clocks on F5 refresh/reload.
clevengr Jan 3, 2025
983ff8d
i1027, i1029: update "contest is running" req for viewing clars & probs:
Jan 5, 2025
b069333
i1027: add code to resync UI clocks with PC2 periodically.
Jan 5, 2025
d8d0c7c
i1027: constants.ts: set UI clock resync interval to every 15 mins.
Jan 5, 2025
db34af4
i1027: source code cleanup (remove outdated comments, etc.)
Jan 5, 2025
50aceab
i1027: added documentation. No code changes.
Jan 5, 2025
5702c96
i1027: use "start time" not elapsed secs to determine "started"; also:
Jan 6, 2025
b92c51c
i1027: allow problem display if contest started (not just if running).
Jan 7, 2025
f7ca705
i1027: pass ContestService to ContestTimer constructor; add "TODO" note.
Jan 7, 2025
a9273b0
i1027: Add code to handle time elapsed while browser is minimized; also:
Jan 7, 2025
7274551
i1027: rename method to updateLocalContestClockFromServer()
clevengr Jan 8, 2025
dec524c
i1027: ContestTimer: always update "mostRecentTimerUpdate"; also:
clevengr Jan 8, 2025
046b421
i1027: removed multiple no-longer-needed debugging outputs.
Jan 11, 2025
fcd4a06
i1027: added debug output.
Jan 11, 2025
3465ccc
i1027: added documentation (no code changes)
Jan 11, 2025
3af4594
i1027: added contest timers to app-header files in "shared" module.
Dec 15, 2024
83d33ab
i1027: added [email protected] to package.json
Dec 15, 2024
9130038
i1027: added various attempts to get HTML properly displaying clocks.
Dec 17, 2024
8ef6949
i1027: added setInterval object to gen timer ticks; added date func.
Dec 17, 2024
84246f0
i1027: added attempts to access new ElapsedTimePipe from core module.
Dec 17, 2024
a21c90b
i1027: added attempts to access ElapsedTimePipe from shared module.
Dec 17, 2024
75604ce
i1027: added new ElapsedTimePipe for formatting dates into dd:hh:mm:ss.
Dec 17, 2024
31e5afc
i1027: added method getContestClock() to ContestServices (to both...
clevengr Dec 18, 2024
4e22cce
i1027: added ContestClockModel to hold the WTI-API rep of ContestTime.
clevengr Dec 18, 2024
6679a44
i1027: added support for getting contest start time via the PC2 API.
clevengr Dec 18, 2024
1b901ce
i1027: add support for URL /contestclock to WTI-API server.
clevengr Dec 18, 2024
6e88d1e
i1027: fix minor typo in javadoc comment.
clevengr Dec 18, 2024
400ae36
i1027: add elapsedTimePipe to core/services -- an Angular "Pipe" that...
clevengr Dec 18, 2024
a38ade1
i1027: clean up app-header HTML to display elapsed time in two ways:
clevengr Dec 18, 2024
7f6b45e
i1027: minor doc (comments) added.
clevengr Dec 21, 2024
b1ef346
i1027: added newly-required getContestClock() method to mock service.
clevengr Dec 21, 2024
ce3f22e
i1027: added fetch of PC2 contest clock upon successful login.
clevengr Dec 21, 2024
66b40ae
i1027: removed no-longer-needed debugging; reformat code for readability
clevengr Dec 21, 2024
5098a2d
i1027: rename time display pipe, rework to accept numSeconds
clevengr Dec 22, 2024
f446c22
i1027: add WTI-UI model for contest clock.
clevengr Dec 24, 2024
3ff1b6e
i1027: added Timer class to enable/disable incr of display times.
clevengr Dec 24, 2024
40d6d4f
i1027: remove no-longer-used ngx-countdown.
clevengr Dec 25, 2024
2b84408
i1027: update for compatibility with PR for i871.
clevengr Dec 26, 2024
c7da8c6
i1027: SharedModule: remove ngx-countdown; add ContestServices.
clevengr Dec 26, 2024
7674bc8
i1027: rename contestClock Subject to contestClockEvent; add debugging
clevengr Dec 26, 2024
a811667
i1027: LoginPage: move ContestClock to ContestService; add doc and debug
clevengr Dec 26, 2024
53797b5
i1027: IWebSocketService: make root-injectable; add debug output.
clevengr Dec 26, 2024
f3b8a84
i1027: IContestService: make root-injectable; also:
clevengr Dec 26, 2024
5060f19
i1027: DisplayTimePipe: added debug output.
clevengr Dec 26, 2024
b5dcf6a
i1027: ContestTimerService: added error output when remainingTime<0
clevengr Dec 26, 2024
ebf4449
i1027: ContestService: make root-injectable; add ContestTimer; also:
clevengr Dec 26, 2024
64af0d2
i1027: ContestMockService: add mock support for updating contest clock
clevengr Dec 26, 2024
133ef89
i1027: AppComponent: add support matching i871-F5-in-WTI.
clevengr Dec 26, 2024
67fa13a
i1027: moved timer support out of AppHeader (into ContestService); also:
clevengr Dec 26, 2024
98ff437
i1027: ContestTimerService: added conditional debugging output.
clevengr Dec 27, 2024
e1558d2
i1027: ContestService: added conditional debugging output.
clevengr Dec 27, 2024
52a12eb
i1027: ContestService: return elapsed/remainging from Timer.
clevengr Dec 27, 2024
62cd488
i1027: LoginPageComponent: added conditional debugging output.
clevengr Dec 27, 2024
8668e00
i1027: AppHeaderComponent: added conditional debugging output.
clevengr Dec 27, 2024
94a8e06
i1027: ContestClock: added additional documentation.
clevengr Dec 27, 2024
df9f746
i1027: commented-out no-longer-needed debug output.
clevengr Dec 27, 2024
a9796f1
i1027: updated debug comments; added "todo".
clevengr Dec 27, 2024
67133db
i1027: refactor updateContestClock() to be a ContestService method.
clevengr Dec 28, 2024
81ebbd6
i1027: IWebsocketService: invoke updateContestClock() on 'clock' msg.
clevengr Dec 28, 2024
b8a3e0a
i1027: LoginPageComponent: add comments (no code changes).
clevengr Dec 28, 2024
5f35f42
i127: AppHeaderComponent: remove debugging display of raw seconds.
clevengr Dec 28, 2024
c2f1b90
i1027: WTI-API.ContestController: fix copy/paste typo in response.
clevengr Dec 28, 2024
ec7c051
i1027: ContestTimer: set default remaining time to zero. Note that...
clevengr Dec 28, 2024
9b8f0ea
i1027: added comments (no code changes).
clevengr Jan 1, 2025
3454d60
i1027: added toString() method. This is important because...
clevengr Jan 1, 2025
b11da0b
i1027: handle wallClockStartTime when contest hasn't been started; also,
clevengr Jan 2, 2025
18f2ce1
i1027: update contest clocks on F5 refresh/reload.
clevengr Jan 3, 2025
4d05cf0
i1027, i1029: update "contest is running" req for viewing clars & probs:
Jan 5, 2025
6d19050
i1027: add code to resync UI clocks with PC2 periodically.
Jan 5, 2025
7c7298a
i1027: constants.ts: set UI clock resync interval to every 15 mins.
Jan 5, 2025
9fbef22
i1027: source code cleanup (remove outdated comments, etc.)
Jan 5, 2025
41f8289
i1027: added documentation. No code changes.
Jan 5, 2025
76ae5e2
i1027: use "start time" not elapsed secs to determine "started"; also:
Jan 6, 2025
b41033f
i1027: allow problem display if contest started (not just if running).
Jan 7, 2025
51ab222
i1027: pass ContestService to ContestTimer constructor; add "TODO" note.
Jan 7, 2025
92eefb5
i1027: Add code to handle time elapsed while browser is minimized; also:
Jan 7, 2025
09163e6
i1027: rename method to updateLocalContestClockFromServer()
clevengr Jan 8, 2025
17a0819
i1027: ContestTimer: always update "mostRecentTimerUpdate"; also:
clevengr Jan 8, 2025
f0d9ee3
i1027: removed multiple no-longer-needed debugging outputs.
Jan 11, 2025
bc5bf55
i1027: added debug output.
Jan 11, 2025
e0ff6d1
i1027: added documentation (no code changes)
Jan 11, 2025
25a7f21
i1027: update core.module.ts as part of rebase onto develop.
Jan 11, 2025
b98c837
i1027: fix conflict errors during rebase.
Jan 11, 2025
0996489
i1027: fix merge conflicts:
Jan 11, 2025
515bfb2
i1027: updated data types as requested in PR review.
clevengr Feb 6, 2025
8af6b4c
i1027: change data types, remove debug prints for clarity.
clevengr Feb 6, 2025
89e3952
i1027: fix data types, remove debug prints, minor reformatting.
clevengr Feb 6, 2025
012426e
i1027: requested updates per PR review, including:
clevengr Feb 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 117 additions & 12 deletions projects/WTI-API/src/main/controllers/ContestController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
Expand All @@ -25,6 +26,8 @@
import config.ServerInit;
import edu.csus.ecs.pc2.api.IClarification;
import edu.csus.ecs.pc2.api.IClient;
import edu.csus.ecs.pc2.api.IContest;
import edu.csus.ecs.pc2.api.IContestClock;
import edu.csus.ecs.pc2.api.IJudgement;
import edu.csus.ecs.pc2.api.ILanguage;
import edu.csus.ecs.pc2.api.IProblem;
Expand All @@ -48,6 +51,7 @@
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.Authorization;
import models.ClarificationModel;
import models.ContestClockModel;
import models.LanguageModel;
import models.ProblemModel;
import models.ServerErrorResponseModel;
Expand Down Expand Up @@ -325,16 +329,23 @@ public Response problems(
if (userInformation == null) {
throw new NotLoggedInException();
} else {
// make sure that the contest is running (problems are not allowed to be seen when the contest is not running)
if (!userInformation.getContest().isContestClockRunning()) {
// make sure that the contest has been started (problems are not allowed to be seen before the contest starts)
// Note that starting the contest results in setting the "contest start time" in the ContestClock object to
// the Unix Epoch time at which the contest was started; prior to starting the contest that value will be null.
if (userInformation.getContest().getContestClock().getContestStartTime()==null) {
return Response.status(Response.Status.UNAUTHORIZED).entity(
new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request"))
new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request - contest has not started."))
.type(MediaType.APPLICATION_JSON).build();
}
}
} catch (NotLoggedInException e1) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity(new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request"))
.entity(new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request -- not logged in."))
.type(MediaType.APPLICATION_JSON).build();
} catch (Exception e2) {
logger.severe("Exception in ContestController /problems endppoint: " + e2.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new ServerErrorResponseModel(Response.Status.INTERNAL_SERVER_ERROR, "Exception in ContestController /problems endppoint: " + e2.getMessage()))
.type(MediaType.APPLICATION_JSON).build();
}

Expand Down Expand Up @@ -411,6 +422,107 @@ public Response isRunning(
.type(MediaType.APPLICATION_JSON).build();
}


/***
* This method returns a "ContestClock" object encapsulating the current state of the PC2 contest clock
* as maintained by the PC2 class "ContestTime".
* It first checks to see that the user (team) specified by the received "key" is currently logged in. If so, the method
* returns the current contest time information, obtained from the PC2 {@link ServerConnection} for the team.
*
* @param key a String containing a key which uniquely identifies the team making the request.
* The value of "key" is obtained from the HTTP header parameter "team_id".
*
* @return Response of:
* 401 (unauthorized) if team's credentials are incorrect (i.e. the team is not logged in or is not allowed to make such a request);
* 500 (INTERNAL_SERVER_ERROR) if an error occurs in fetching the requested data from the PC2 server;
* otherwise 200 (OK) and a JSON string containing a {@link ContestClock} is returned.
*/
@Path("/contestclock")
@GET
@ApiOperation(value = "contestclock",
notes = "Gets the PC2 contest clock (time info).")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns a contest clock object.", response = ContestClockModel.class),
@ApiResponse(code = 401, message = "Returned if invalid credentials are supplied", response = ServerErrorResponseModel.class)
})

public Response contestClock(@ApiParam(value="token used by logged in users to access teams information",
required = true) @HeaderParam("team_id")String key) {

if (connections == null) {
System.err.println ("SEVERE: ContestController.contestClock(): team connections map in MainController is null!");
logger.severe("ContestController.contestClock(): team connections map in MainController is null!");
throw new NullPointerException("Team connections map in MainController is null in ContestController.contestClock()");
}
ServerConnection userInformation = connections.get(key);

//verify the user is logged in
try {
// make sure we have connection information for this user (i.e. that the user is logged in)
if (userInformation == null) {
throw new NotLoggedInException();
}
} catch (NotLoggedInException e1) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity(new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request"))
.type(MediaType.APPLICATION_JSON).build();
}

//the ContestClock object to be returned in the response to the HTTP request
ContestClockModel returnableContestClock ;

try {
//get the contest clock from the PC2 Server via the PC2 API ServerConnection
// IContestClock contestClock = userInformation.getContest().getContestClock();

IContest contest = userInformation.getContest();
if (contest == null) {
System.err.println ("SEVERE: ContestController.contestClock(): ServerConnection.getContest() returned null!");
logger.severe("ContestController.contestClock(): ServerConnection.getContest() returned null!");
throw new NullPointerException("ServerConnection.getContest() returned null in ContestController.contestClock()");
}
IContestClock contestClock = contest.getContestClock();
if (contestClock == null) {
System.err.println ("SEVERE: ContestController.contestClock(): ServerConnection.getContest().getContestClock() returned null!");
logger.severe("ContestController.contestClock(): ServerConnection.getContest().getContestClock() returned null!");
throw new NullPointerException("ServerConnection.getContest().getContestClock() returned null in ContestController.contestClock()");
}

//retrieve the relevant fields from the PC2 contest clock
boolean isRunning = contestClock.isContestClockRunning();
long contestLengthInSecs = contestClock.getContestLengthSecs();
long elapsedSecs = contestClock.getElapsedSecs();
long wallClockStartTime ;
//"contest start time" is a Calendar object and might be null if the contest has never been started
Calendar startTimeCalendar = contestClock.getContestStartTime();
if (startTimeCalendar == null) {
wallClockStartTime = 0;
} else {
wallClockStartTime = startTimeCalendar.getTimeInMillis();
}

//construct a ContestClock containing the PC2 Server clock values
returnableContestClock = new ContestClockModel(isRunning,contestLengthInSecs, elapsedSecs, wallClockStartTime);

}
catch(NotLoggedInException e) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity(new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request"))
.type(MediaType.APPLICATION_JSON).build();
}
catch(NullPointerException e) {
logger.severe(e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new ServerErrorResponseModel(Response.Status.INTERNAL_SERVER_ERROR, "NullPointerException in ContestController.contestClock()"))
.type(MediaType.APPLICATION_JSON).build();
}

//return the PC2 ContestClock, mapped into JSON
return Response.ok()
.entity(returnableContestClock)
.type(MediaType.APPLICATION_JSON).build();
}

/***
* This method returns a list of all the clarifications in the PC^2 contest submitted by the specified team.
* It first checks to see that the user (team) specified by the received "key" is currently logged in and that the
Expand Down Expand Up @@ -444,17 +556,10 @@ public Response clarifications(@ApiParam(value="token used by logged in users to
// make sure we have connection information for this user (i.e. that the user is logged in)
if (userInformation == null) {
throw new NotLoggedInException();
} else {
// make sure that the contest is running (problems are not allowed to be seen when the contest is not running)
if (!userInformation.getContest().isContestClockRunning()) {
return Response.status(Response.Status.UNAUTHORIZED).entity(
new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request"))
.type(MediaType.APPLICATION_JSON).build();
}
}
} catch (NotLoggedInException e1) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity(new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request"))
.entity(new ServerErrorResponseModel(Response.Status.UNAUTHORIZED, "Unauthorized user request - not logged in."))
.type(MediaType.APPLICATION_JSON).build();
}

Expand Down
44 changes: 44 additions & 0 deletions projects/WTI-API/src/main/models/ContestClockModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package models;

import edu.csus.ecs.pc2.core.model.ContestTime;

/**
* This class encapsulates the features of the PC2 "contest clock" (class {@link ContestTime}) which need to be able to be passed
* to the WTI-UI.
*
* @author JohnC
*
*/
public class ContestClockModel {

private boolean isRunning;
private long contestLengthInSecs;
private long elapsedSecs; //total time in seconds that the contest has been running -- does NOT include time during any "pauses"
private long wallClockStartTime; //unix timestamp when the contest actually started (msec since the Epoch),
// or zero if contest has not ever been started. Does not change due to "pauses".

public ContestClockModel(boolean isRunning, long contestLengthInSecs, long elapsedSecs, long wallClockStartTime) {
this.isRunning = isRunning;
this.contestLengthInSecs = contestLengthInSecs;
this.elapsedSecs = elapsedSecs;
this.wallClockStartTime = wallClockStartTime;
}

public ContestClockModel() { }

public boolean isRunning() {
return isRunning;
}

public long getContestLengthInSecs() {
return contestLengthInSecs;
}

public long getElapsedSecs() {
return elapsedSecs;
}

public long getWallClockStartTime() {
return wallClockStartTime;
}
}
11 changes: 11 additions & 0 deletions projects/WTI-UI/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ import { LogoutComponent } from './modules/login/components/logout/logout.compon
import { ClarificationsPageComponent } from './modules/clarifications/components/clarifications-page/clarifications-page.component';
import { ScoreboardPageComponent } from './modules/scoreboard/components/scoreboard-page/scoreboard-page.component';

/**
* This class defines the "routes" which the Angular application (specifically, the Angular Router) knows how to follow.
* Note that ORDER MATTERS; the Router searches the 'Routes' array from the beginning to find a route which matches the
* path it has been told to attempt to follow. For this reason, the "login" page will always be the default route.
* Note that remaining routes are protected by a "canActivate: [AuthGuard]" clause; the effect of this is that if the Router
* decides to attempt to follow that route, it first invokes the "AuthGuard" class, which determines whether following that
* route is 'allowed'. (The criterion defined in "AuthGuard" is that the user is logged in.)
* If the route specified by the user does not match any other route defined here, the Router defaults to the LAST route,
* the one with a "path" of "**". The effect of this is that any attempt by the user to enter any undefined route in the
* browser will cause a transfer to the "/runs" page.
*/
const routes: Routes = [
{
path: 'login',
Expand Down
9 changes: 7 additions & 2 deletions projects/WTI-UI/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export class AppComponent implements OnInit {
console.log (this._contestService);
}
this._contestService.isContestRunning = val;
this._contestService.contestClock.next();
this._contestService.contestClockEvent.next();
});

//get the most recently saved Option values from sessionStorage
Expand All @@ -180,14 +180,19 @@ export class AppComponent implements OnInit {
}
}

//restore the contest clock on-screen display values (elapsed and remaining)
if (DEBUG_MODE) {
console.log ('Updating local contest clock values from server');
}
this._contestService.updateLocalContestClockFromServer();

// transfer to the (former) "current page".
let page = getCurrentPage();
if (DEBUG_MODE) {
console.log ('...navigating to previous page: ' + page);
}

//navigate to the most recently saved page
// TODO: consider whether using history.pushState()/popState() is a better solution for this...
this.router.navigate([page])
.then(nav => {
if (DEBUG_MODE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,36 @@ import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { ContestLanguage } from '../models/contest-language';
import { ContestProblem } from '../models/contest-problem';
import { ContestClock } from '../models/contest-clock';
import { Clarification } from '../models/clarification';
import { DEBUG_MODE } from 'src/constants';

@Injectable({
providedIn: 'root' //forces the service to be a singleton across all app components ('root' == "root injector")
})
export abstract class IContestService {

//need to document how the "clarificationsUpdated" Subject works (see the description of contestClockEvent, below;
//this operates similarly although it is used in more places)
clarificationsUpdated = new Subject<void>();
contestClock = new Subject<void>();

//The contestClockEvent "Subject" is used as a toggle to indicate when contest clock-related events have occured.
//It is "subscribed to" by the "ProblemSelectorComponent.ngOnInit()" method (meaning, that component will get a callback
//whenever the contestClockEvent's "next()" method is invoked). The contestClockEvent's "next()" method is invoked in exactly one
//place: IWebsocketService.incomingMessage(), when a message of type "contest_clock" is received by the WTI-UI from the WTI-API
//(which in turn happens when the WTI-API receives a "contest clock configuration update" notice via the PC2 API).
//The effect of all this is that the ProblemSelectorComponent gets notified when "some change" has occurred in the state of the
//PC2 contest clock. This causes the ProblemSelectorComponent to execute its "loadProblems()" method; that method in turn checks
//whether the contest is currently RUNNING; if so, it loads the problem names; if not, it blanks out problem names.
//note that contestClockEvent is a WTI-UI-specific object used to coordinate with Websocket "clock" messages;
//it is NOT a "ContestClock" object in the sense of classes defined in core/models, nor an "Event" it PC2 terms.
contestClockEvent = new Subject<void>();

standingsUpdated = new Subject<void>();
isContestRunning = false;

//the WTI-UI representation of the PC2 Server's ContestClock (PC2 class ContestTime)
contestClock: ContestClock = new ContestClock();

//give each instance of IContestService a unique ID for debugging purposes
private static nextId: number = 1;
Expand All @@ -35,7 +54,15 @@ export abstract class IContestService {
abstract getClarifications(): Observable<Clarification[]>;

abstract getIsContestRunning(): Observable<boolean>;

abstract getContestClock(): Observable<ContestClock>; //note this refers to a "ContestClock" model object, not the "contestClockEvent" Subject above

abstract updateLocalContestClockFromServer (): void; //update the WTI-UI representation of the PC2 contest clock

abstract getElapsedSecs(): number;

abstract getRemainingSecs(): number;

abstract getStandings(): Observable<String>;

abstract markStandingsOutOfDate(): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,28 @@ export abstract class IWebsocketService {
break;
}
case 'contest_clock': {
/* This case is invoked when the WTI Server gets a "Contest_Clock" message from the PC2 Server
* (via the PC2 API "ConfigurationItemUpdated()" listener in the WTI class "ConfigurationService").
* Such a message from the PC2 Server causes the WTI Server to send a "contest_clock" message through
* the websocket to this client.
*/
if (DEBUG_MODE) {
console.log ("Got '", message.type, "' websocket message in IWebsocketService.incomingMessage()");
console.log ("IWebsocketService.incomingMessage(): got websocket message '", message.type);
console.log ("IWebsocketService.incomingMessage(): invoking ContestService.getIsContestRunning() and");
console.log (" subscribing to callback from HTTP GET");
}
this._contestService.getIsContestRunning()
.subscribe((val: any) => {
if (DEBUG_MODE) {
console.log ("IWebsocketService.incomingMessage(): callback from ContestService.getIsContestRunning() returned '", val, "'");
console.log ("Setting ContestService.isContestRunning to '", val, "'") ;
console.log (" and invoking ContestService.contestClock.next()") ;
console.log ("Setting ContestService.isContestRunning to '", val, "',") ;
console.log (" invoking ContestService.contestClockEvent.next()") ;
console.log (" and invoking ContestService.updateLocalContestClockFromServer()");
}

this._contestService.isContestRunning = val;
this._contestService.contestClock.next();
this._contestService.contestClockEvent.next();
this._contestService.updateLocalContestClockFromServer();
});
break;
}
Expand Down
10 changes: 8 additions & 2 deletions projects/WTI-UI/src/app/modules/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IWebsocketService } from './abstract-services/i-websocket.service';
import { UiHelperService } from './services/ui-helper.service';
import { SharedModule } from '../shared/shared.module';
import { DEBUG_MODE } from 'src/constants';
import { DisplayTimePipe } from './services/displayTimePipe.service';

export function TeamsServiceFactory(http: HttpClient) {
if (DEBUG_MODE) {
Expand Down Expand Up @@ -83,18 +84,23 @@ export function WebsocketServiceFactory(injector: Injector,
providers: [
{ provide: ITeamsService, useFactory: TeamsServiceFactory, deps: [HttpClient] },
{ provide: IContestService, useFactory: ContestServiceFactory, deps: [HttpClient] },
{ provide: ContestService, useFactory: ContestServiceFactory, deps: [HttpClient] },
{ provide: AuthService, useClass: AuthService },
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: IWebsocketService, useFactory: WebsocketServiceFactory, deps: [Injector, UiHelperService, IContestService, ITeamsService, AuthService] },
AuthGuard,
DisplayTimePipe,
//TODO: should the following two still be declared here since they are now listed in the above "deps" list?
UiHelperService,
ContestService
],
imports: [
HttpClientModule,
SharedModule
SharedModule,
DisplayTimePipe
],
exports: [],
exports: [
DisplayTimePipe
]
})
export class CoreModule { }
Loading