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

I843 CLICS API 2023-06 Endpoint Compliance #864

Open
wants to merge 187 commits into
base: develop
Choose a base branch
from

Conversation

johnbrvc
Copy link
Collaborator

@johnbrvc johnbrvc commented Nov 16, 2023

Description of what the PR does

Implements version 2023-06 of the CLICS API specification endpoint services.
Creates a mechanism for adding newer (and older) versions of the CLICS specification relatively easily by creating a package for each CLICS version.
Adds PC2 account roles to endpoint service access.

Issue which the PR addresses

Partial work towards Issue #843 and these issues: #630 #697 #704 #709 #710 #712 #720 #787 #537 #562 #837 #840 #1016 #1048

Environment in which the PR was developed (OS,IDE, Java version, etc.)

Windows 11
Java 1.8.581

Helpful information for reviewing the PR

  1. The majority of the files related to this PR are in the src/edu/csus/ecs/pc2/clics/ folder.
  2. Specifically, the majority of those files are in the src/edu/csus/ecs/pc2/clics/API202306 folder (package).
  3. The files in the src/edu/csus/pc2/clics/API202003 folder (package) have been relocated from the src/edu/csus/ecs/pc2/web folder (package). The idea is that each API version will have its own package under the src/edu/csus/ecs/pc2/clics folder. Nomenclature can be changed if folks do not like it as is.
  4. Files in other folders (core, model, imports/ccs) contain either bug fixes or additional methods or changes to methods to support the new CLICS API Java package structure.
  5. The src/edu/csus/ecs/pc2/util/JSONTool.java file has had many of its utility methods changed to public static (since they do not depend on an object instance), and, the new CLICS API Java package structure allows the methods to be shared easily without instantiating an object. Maybe JSONTool methods should be moved to JSONUtilities ?
  6. Files in the services folders (core, eventFeed, web, ui) were changed to support selection of different API versions from the Event Feed thick client GUI.

Precise steps for testing the PR (i.e., how to demonstrate that it works correctly)

Right now, it's lots of manual web page queries. The following endpoints should work and return information compliant with 2023-06. Other endpoints will return whatever we used to return. (Note: Replace the contestId below, Default-5639045423021663843 with whatever contest ID you are testing with).

Of course, you have to run an event-feed client, and select 2023-06 as the API version, then start the web services.
Selecting 2020-03 will make the web services behave like they always did in the past (whatever API version that was).

You can change the links below to something like:
https://judge2:judge2@localhostt:50443/contests/Default-5639045423021663843/problems
or
https://team8:team8@localhostt:50443/contests/Default-5639045423021663843/problems
or
https://admin:admin@localhostt:50443/contests/Default-5639045423021663843/problems
to test out how the endpoints behave for various roles. The first 2 above use accounts from the accounts list configured into PC2. The last one uses a role from the realms.properties file. Both types of login information are accepted. Obviously, only PC2 judges, teams and administrator accounts are added to the realm as acceptable login ID's.

https://localhost:50443/
https://localhost:50443/contests
https://localhost:50443/contests/Default-5639045423021663843
https://localhost:50443/contests/Default-5639045423021663843/access
https://localhost:50443/contests/Default-5639045423021663843/account
https://localhost:50443/contests/Default-5639045423021663843/accounts
https://localhost:50443/contests/Default-5639045423021663843/accounts/AccountID
https://localhost:50443/contests/Default-5639045423021663843/teams
https://localhost:50443/contests/Default-5639045423021663843/teams/TEAMID
https://localhost:50443/contests/Default-5639045423021663843/problems
https://localhost:50443/contests/Default-5639045423021663843/problems/PROBLEMID
https://localhost:50443/contests/Default-5639045423021663843/judgement-types
https://localhost:50443/contests/Default-5639045423021663843/judgement-types/JUDGEMENTTYPE
https://localhost:50443/contests/Default-5639045423021663843/clarifications
https://localhost:50443/contests/Default-5639045423021663843/clarifications/CLARID
https://localhost:50443/contests/Default-5639045423021663843/groups
https://localhost:50443/contests/Default-5639045423021663843/groups/GROUPID
https://localhost:50443/contests/Default-5639045423021663843/organizations
https://localhost:50443/contests/Default-5639045423021663843/organizations/ORGID
https://localhost:50443/contests/Default-5639045423021663843/judgements
https://localhost:50443/contests/Default-5639045423021663843/judgements/JUDGEMENTID
https://localhost:50443/contests/Default-5639045423021663843/runs
https://localhost:50443/contests/Default-5639045423021663843/runs/TESTCASEID
https://localhost:50443/contests/Default-5639045423021663843/submissions
https://localhost:50443/contests/Default-5639045423021663843/submissions/RUNID
https://localhost:50443/contests/Default-5639045423021663843/submissions/TESTCASEID/files
https://localhost:50443/contests/Default-5639045423021663843/languages
https://localhost:50443/contests/Default-5639045423021663843/languages/LANGID
https://localhost:50443/contests/Default-5639045423021663843/state
https://localhost:50443/contests/Default-5639045423021663843/awards
https://localhost:50443/contests/Default-5639045423021663843/scoreboard
https://localhost:50443/contests/Default-5639045423021663843/scoreboard?group_id=GROUPID
https://localhost:50443/contests/Default-5639045423021663843/scoreboard?division=# (*)

(*) - The division query parameter is a PC2 extension and is NOT part of the CLICS API specification. It is not prohibited by the specification to add additional query parameters. Since PC2 supports the notion of divisions, eg groups with "D1", "D2", etc. in their names, the division query parameter allows filtering of the scoreboard by division.

PATCH requests work for contests/contestid/ endpoints as per the spec. Certain things, like countdown_pause_time are parsed and validated, but the back end code to implement them in PC2 is not present yet. eg. stop the countdown clock and display the countdown pause time, resume countdown, etc.

POST requests work for contests/contestid/clarifications endpoint to add new clarifications. PUT is not implemented and there are no plans to do so since it requires reworking object ID's (PUT allows a user to specify the internal ID of a clarification)

POST/PUT/PATCH are not implemented for awards endpoint. A NON_IMPLEMENTED error is returned to the client.

The access endpoint only returns properties for those services that are implemented for 2023-06 so far (listed above).

The following endpoint services are not applicable to a CCS and will NOT be implemented:

  • persons
  • commentary

@johnbrvc johnbrvc added CLICS CCS Update Changes to update to the CLICS specification Comments/Analysis Requested Soliciting comments and feedback from the Team about how to implement issue In PR code review Primary CCS Required for PC2 to act as primary CCS labels Nov 16, 2023
@johnbrvc johnbrvc self-assigned this Nov 16, 2023
Copy link
Contributor

@troy2914 troy2914 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code review, left some comments.

Copy link
Contributor

@troy2914 troy2914 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems this was submitted in error.

Copy link
Contributor

@troy2914 troy2914 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add new statements to one in samps too.

@troy2914 troy2914 added this to the 9.10.0 milestone Nov 24, 2023
Copy link
Contributor

@troy2914 troy2914 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed new changes. seems good, other than a old hack.

@johnbrvc johnbrvc changed the base branch from v9.10 to develop November 30, 2023 21:37
Copy link
Contributor

@troy2914 troy2914 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still have a few outstanding comments.

@troy2914
Copy link
Contributor

btw, you have to expand the expansions on github to see all the comments that I have left.

@johnbrvc johnbrvc changed the title I843 CLICS API 2023-06 Compliance I843 CLICS API 2023-06 Endpoint Compliance Dec 25, 2023
Copy link
Contributor

@troy2914 troy2914 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These latest commits look normal to me.

@troy2914
Copy link
Contributor

hmm, just looking at the commits, if seems you pull'ed some changes from develop, instead of rebase develop.

@johnbrvc
Copy link
Collaborator Author

johnbrvc commented Feb 3, 2024

still have a few outstanding comments.

I believe I have addressed all the things that require code changes.

@troy2914 troy2914 modified the milestones: 9.10.0, 9.11.0 Feb 27, 2024
@johnbrvc johnbrvc force-pushed the i843_CLICS_API_Endpoint_Compliance branch from 828ca5a to e0b0bec Compare December 10, 2024 16:06
@clevengr clevengr removed the request for review from lane55 February 15, 2025 01:10
Added some comments and changed variable name to clarify freeze time calculation in CLICSContestState.
Validate properties array for CLICSEndpoint.
Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSContestState.java; looks good to me.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSOrganization.java; left comments. Nothing huge that I couldn't live with...

Comment on lines 19 to 20
* CLICS Organization
* Contains information about an Organization (institution)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as on other files, about JavaDoc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

* Fill in organization information properties
* TODO: fix to use organization obtained from organizations.json. From insitutions.tsv, we get this:
* [0]:1329 [1]:New York University [2]:NYU
* but only need [1]. We get the rest from the a typical account.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what ...from the a typical account is trying to say. And is it talking about a PC2 account?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified the comment in next push.

Comment on lines 71 to 72
* @param account typical account using this organization
* @param orgFields String array of fields that correspond to institutions.tsv. Gack!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orgId is missing from the @param list...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

* [0]:1329 [1]:New York University [2]:NYU
* but only need [1]. We get the rest from the a typical account.
*
* @param account typical account using this organization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not at all sure what typical account using this organization means. How does an account "use" an organization? (Does this mean, for example, the kind of account which would normally login on behalf of the organization? Or does it mean the kind of CCS-internal account that would somehow "manipulate" this organization?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I clarified the comment. Basically, it's an account that is associated with the organization. Several accounts can "use" the same organization, eg. multiple teams from the same school use the same Organization.

Comment on lines +75 to +76
id = orgId;
icpc_id = orgId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird that orgId is assigned to both these fields. If we don't have any notion of "organization" within PC2, then perhaps one of these should be NULL instead of set to the value of the other? (I'm not sure where this gets used, so I'm a bit stabbing in the dark; it just seems weird to me to assign the same thing to two different fields when those two fields clearly exist for some separate reasons...)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In all cases that I can think of, the id and icpc_id are the same, at least for DOMjudge and PC2. The id property can be anything a CCS wants. We (PC2) just happen to say we're going to use the CMS ID (which is the icpc_id) for the id. Perfectly legal.

ObjectMapper mapper = JSONUtilities.getObjectMapper();
return mapper.writeValueAsString(this);
} catch (Exception e) {
return "Error creating JSON for organization info " + e.getMessage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in next push.

institutionsMap = createInstitutionsFromJSON(mapper.readValue(jsonfile, CLICSOrganization[].class), log);
} catch (Exception e) {
// deserialize exceptions
log.log(Log.WARNING, "could not deserialize organizations file " + jsonfile.toString(), e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to log message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

institutionsMap = createInstitutionsFromJSON(mapper.readValue(json, CLICSOrganization[].class), log);
// deserialize exceptions
} catch (Exception e) {
log.log(Log.WARNING, "could not deserialize organizations string", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to log message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

}

/**
* Convert CLICS organizations to PC2 hashma
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's a "hashma" :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't tell me you've never heard of a hashma ? It's the "mother" of all Hash Maps! (I changed it to the lesser, hashmap, in the comment for the next push).

for(CLICSOrganization org: corgs) {

if(StringUtilities.isEmpty(org.id)) {
log.log(Log.SEVERE, "no id property in organization");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to log message?

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSProblem.java; minor comments.

Comment on lines 15 to 16
* CLICS Problem
* Contains information about a Problem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments about JavaDoc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. As I mention below, I will fix these comments on related classes in the next push.

ObjectMapper mapper = JSONUtilities.getObjectMapper();
return mapper.writeValueAsString(this);
} catch (Exception e) {
return "Error creating JSON for problem info " + e.getMessage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to error message. Also, shouldn't this be logged?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added CLICS to message in next PR.

We will have to discuss at next week's meeting the general concept of logging every error that the CLICS API can generate for faulty data it receives. I'm not sure that's a good or useful idea.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSProvider.java; minor comments.

Comment on lines 13 to 14
* CLICS Score (for a problem)
* Contains information about the score a team received for a single problem
Copy link
Contributor

@clevengr clevengr Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments about JavaDoc.

Also, since this class is the PC2 implementation of what the CLICS spec refers to as "Problem Data" (very poor name, I agree -- but that's what's in the spec), it would probably be useful to have a comment in this header that mentions the fact that this class corresponds to what in CLICS is called "Problem data"...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

* @param versionInfo
*/
public CLICSProvider(VersionInfo versionInfo) {
name = "pc2";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this perhaps be upper case ("PC2")? Seems a bit more formal...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Changed in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted to review src/edu/csus/ecs/pc2/clics/API202306/CLICSRun.java. However, I ran into so many illogical things that I suspended the review. See my comments for details. I look forward to something that tells me whether the issue is with the code or with my review process...

Comment on lines 22 to 23
* CLICS Judgment Run
* Contains information about a Run
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about JavaDoc. Also, I'm not sure what the meaning of Judgment Run in the first line is.

Also, I think it would be helpful (for future developers) to add a note here about the difference between the term "Run" in CLICS (execution of a single test case) and "Run" in PC2 (a submission from a team).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CLICSRun class is for the "Judgements" API endpoint.. I thought "Judgements" was too similar to "Judgement Types", so I decided to call the class CLICSRun instead of CLICSJudgement to avoid confusion with CLICSJudgementType. So, we have a CLICSSubmission, which is the contestant submission and the CLICSRun which is the disposition of a Submission (Accepted, Wrong, RTE, etc.)

Now, there is also something in CLICS called "Runs", which PC2 calls "Test Cases", and I called the CLICS class CLICSTestCase.

I will update the comments at the top of each of these modules to indicate how each class corresponds to its CLICS endpoint name in the next push.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think naming the class CLICSRun is REALLY confusing. We already have two notions of "run" in the system: a PC2 "run", which is a submission from a team, and a CLICS "run", which is the execution of a single test case. That's confusing enough by itself; to add a THIRD notion of "run" which is not the same as either one of the existing notions but instead is related to the judgement associated with a run (PC2) or the collection of judgements associated with a submission (CLICS) -- seems to me to add a LOT of unnecessary additional confusion.

Further, as you said above, what you've called CLICSRun is "the disposition of a Submission (Accepted, Wrong, RTE, etc.)". The phrase disposition of a Submission seems (to me) to clearly be something that's a Judgement -- that's what happens (that's what you "get back") when you send in a "Submission". So I don't really see why CLICSJudgement is that bad a name. (Yes, I agree that it could be [slightly] potentially confused with CLICSJudgementType, but I think the presence of Type in the latter name makes it clear that the latter class is about types of judgements, whereas a name like CLICSJudgement indicates an instance of a judgement (of some "type").

In any case, I think the negatives of adding yet another meaning of "run" considerably outweigh the (possible) slight confusion between "judgement" and "judgement_type". If you're really adamant that this shouldn't be called CLICSJudgement, I would at least suggest using something that avoids adding confusion by using the term "run". Perhaps something like CLICSSubmissionResult? In any case I really, really think we should avoid introducing yet another meaning for "run"; we've already got enough confusion in the overloaded use of that term.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the time I wrote this last year, it seemed like a good name (CLICSRun). In hindsight, maybe not. I don't care either way, so I renamed it to CLICSJudgement to match the specification name for it. I was going to name it CLICSJudgment (which is the correct spelling for judgment), but I figured it should match the british spelling they chose to use in the CLICS API. In my comments, and my variables, I use the (correct) American English spelling though (judgment). I wish they weren't inconsistent in their language use in the spec. Ex. If they use "judgement" they should also use "colour", but, it's what we are stuck with.

While I am not actively going through and changing PC2 classes and variables to "judgment", when I make new variables or classes I am using "judgment". Whenever I see "judgement" in the code, I just grit my teeth and wonder why it was done that way. Fixed in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSProblemScore.java. I found a number problems, either with the code or with my understanding of what's going on. Looking forward to an explanation of one or the other.

* @param versionInfo
*/
public CLICSProblemScore(HashMap<String, String> probEleToShort, ProblemSummaryInfo psi) {
num_judged = Utilities.nullSafeToInt(psi.getAttempts(), 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to psi.getAttempts() worries me. That "getter" returns the value of the private field attempts in the PSI. Since that field is private, the only way it can have a reasonable (i.e. non-zero) value is if some code somewhere calls PSI.setAttempts(). However, there's not a single reference to PSI.setAttempts() anywhere in the entire PC2 project. So how does the value returned by getAttempts() represent the value which should be stored innum_judged?

It also bothers me that the field attempts in PSI is a string... but that would be workable if someone ever assigned a value to attempts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProblemSummaryInfo is an javax.xml type object. It is used to deserialize XML. Recall the DSA (and NSA) both create an XML representation of the scoreboard. This class deserializes a piece of that XML scoreboard into an object for use in such things as, well, this. As such, the setter methods will not be called when it is used to deserialize XML, but, the fields do get set.

*/
public CLICSProblemScore(HashMap<String, String> probEleToShort, ProblemSummaryInfo psi) {
num_judged = Utilities.nullSafeToInt(psi.getAttempts(), 0);
num_pending = Utilities.nullSafeToInt(psi.getIsPending(), 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue with the call to getIsPending(); as far as I can tell no code anywhere actually sets that value;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

public CLICSProblemScore(HashMap<String, String> probEleToShort, ProblemSummaryInfo psi) {
num_judged = Utilities.nullSafeToInt(psi.getAttempts(), 0);
num_pending = Utilities.nullSafeToInt(psi.getIsPending(), 0);
problem_id = psi.getProblemId();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue with the call to `getProblemId(); as far as I can tell no code anywhere actually sets that value.

if(probEleToShort.containsKey(problem_id)) {
problem_id = probEleToShort.get(problem_id);
}
solved = toBool(psi.getIsSolved(), false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue with the call to `getIsSolved(); as far as I can tell no code anywhere actually sets that value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See Above.

solved = toBool(psi.getIsSolved(), false);
if(solved) {
// Problem solution time is in minutes, so multiply by 60 seconds.
time = ContestTime.formatTime(StringUtilities.getIntegerValue(psi.getSolutionTime(), 0)*60);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the multiplication by 60? The CLICS spec says time is the number of MINUTES into the contest when the problem was solved...

Copy link
Collaborator Author

@johnbrvc johnbrvc Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. That is wrong for CLICS API 2023-06, however, it was right for the draft spec since it's a RELTIME in the draft (HH:MM:SS). I guess I was being a bit ahead of myself here. I reverted it back to plain old minutes, and changed the data type as well. Next commit.

@pc2ccs pc2ccs deleted a comment from johnbrvc Feb 21, 2025
@pc2ccs pc2ccs deleted a comment from johnbrvc Feb 21, 2025
@pc2ccs pc2ccs deleted a comment from johnbrvc Feb 21, 2025
Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSRun.java. Request strong consideration for changing the class name, along with a potential issue regarding assigning a value to the id property (see comments).

private double max_run_time;

/**
* Fill in properties for a Problem description.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't make sense; this class isn't dealing with Problem properties.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, another cut/paste error. It's good you're finding these. I fix them as I see them, but some fall through the cracks. Fixed in next push.

Comment on lines 63 to 64
* @param model The contest
* @param problem The problem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These @param values don't correspond to the actual arguments in the constructor (and those actual arguments are missing from the comments).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy cut/paste. Fixed in next push.

public CLICSRun(IInternalContest model, IInternalController controller, Run submission, Set<String> exceptProps) {
// {"id":"189549","submission_id":"wf2017-32163123xz3132yy","judgement_type_id":"CE","start_time":"2014-06-25T11:22:48.427+01",
// "start_contest_time":"1:22:48.427","end_time":"2014-06-25T11:23:32.481+01","end_contest_time":"1:23:32.481"}
id = submission.getElementId().toString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The id property of this class (assigned by the above statement) is (according to the CLICS spec) supposed to be the id of the judgement, not the submission. As written, the id is getting assigned the ElementId of the submission. I think the id needs to be assigned by (something like) the following pseudo-code:

state = submission.getState();
if (state == Run.RunStates.JUDGED) {
  //the run has been judged, so get the active judgement record
  jr = submission.getJudgementRecord();
  id = jr.getElementId();
} else {
  // i haven't thought about what to do here (the case where the Run hasn't been judged);
 //  -- I think it's "throw an exception", or at least log a severe error, because there should never be
 // a case where this method is called with a Run that hasn't been judged...
}

This way the "id" is the id of the PC2 "judgement record", not the submission id. (It seems clear that it should not be the "submission id", because otherwise what is the separate submission_id field for?)

Now what I haven't thought deeply about is, what's the relationship between a PC2 JudgementRecord and a CLICS "judgement object". But it seems clear to me that, at least, the value assigned to id should NOT be the submission_id (which is what the existing code does...)

I'm also not at all sure that we want to be exposing PC2 "ElementIds" as externally-viewable ids (but I haven't thought that through thoroughly...) Would it make more sense to call submission.getNumber() and use THAT as the id?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some background: I used the same values for "id" and "submission_id" that we have been using for years in the previous spec version (2020-03) - see: convertJudgementToJSON in API202003/JSONTool.java. In an of itself, this is not a valid reason for not changing it, however ... read on.... there IS a reason.

The "submission_id" property IS the submission number. And, that is the ID that is used for CLICSSubmission records (you'll get to that when you review CLICSSubmission). Since the judgementRecord is tied directly to the submission, I suppose the original author figured it made sense to use the submission's unique ID, which I think is reasonable. It also makes looking up the judgment a bit easier for the "\<contestid\>/judgements/\<ID\>" API endpoint.

In addition, if the submission is not yet "judged", (it could be "queued for judgment", "new", or "being judged"), we still have to furnish a "bare-bones"
judgment record, and since PC2 does not HAVE a judgment record yet, we still need a (judgment) id, so the submission's elementId is a reasonable choice, IMHO. In fact, the spec says, that a judgment record may not have a judgement_type_id if there is no "verdict" (and I HATE that word) yet. Basically, judgement_type_id is only "required iff [the] judgement has completed." If there is no "verdict" yet, many of the properties in the (now) CLICSJudgement record will not be serialized in the JSON (the spec has them as ?)

Unless there are strong objections, I'm inclined to leave it "as-is", not only because it has worked for years, but because I don't see a viable alternative for the non-judged case.

Refactor CLICSRun -> CLICSJudgement, and all references.
Various comment and log message clarifications.
Fixed bug in CLICSProblemScore with returning the time as RELTIME instead of int (minutes).
The YAML parser will convert any value it sees with a fractional part (decimal point followed by numbers) to a double value, which is converted to a String in ContestSnakeYAMLLoader.  The causes a problem when that value was parsed as a Long (it caused a NFE).  This fix converts the value to a double using (Double.parseDouble()), then, it typecasts that to a long.  This is OK, since contest durations are in seconds, not fractional parts of  second.  We just ignore the fractional part.
Also fixed JUnit test correctly since the freeze duration will now compare properly without stripping off the leading 0.
This fix will be part of the PR for i843_CLICS_API_Endpoint_Compliance, but can be cherry-picked elsewhere if needed before this is merged.
Do not compare for leading 0 on freeze duration anymore.
Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSRun.java. Request strong consideration for changing the class name, along with a potential issue regarding assigning a value to the id property (see comments).

Comment on lines 13 to 14
* CLICS team score
* Contains information the score for a team
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about JavaDoc... Also, the second line isn't really a complete sentence...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

total_time = Utilities.nullSafeToInt(teamStanding.getPoints(), 0);
if(num_solved > 0) {
// Problem solution time is in minutes, so multiply by 60 seconds.
time = ContestTime.formatTime(StringUtilities.getIntegerValue(teamStanding.getLastSolved(), 0)*60);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noted previously another file where "multiplication by 60" was inappropriate because the expected result was to be in minutes. In this case the CLICS spec doesn't explicitly say in this case what the time units are (it did say "minutes" in the other case I noted), but I would think it should be minutes here as well -- both for consistency (well, I guess that would depend on whether we think the CLICS spec is, or is intended to be, consistent :) as well as that PC2 (and other WF CCS's) apply penalty minutes.

Also, as noted previously, the default value of "0" for getIntegerValue() means that a value of null or empty returned from getLastSolved() means that a team gets a default time of zero, which is an advantage to the team; I'm not sure that's what we should be assigning in that case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the same problem as I had in the other module where I was "ahead of the curve" in implementing the draft spec and not 2023-06. The draft wants a RELTIME here, which is HH:MM:SS, so you'd need the time in seconds. 2023-06 still has it as minutes. The format of time is fixed in the next push. Note that time is a String, as is the value returned by getLastSolved(), so if it's null it will not be included in the serialized JSON, so that point is moot about a "default value". Fixed in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSScoreboard.java. Made several comments, none of which are showstoppers.

Comment on lines 32 to 33
* CLICS Scoreboard
* Contains information about the scoreboard
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaDoc...

state = new CLICSContestState(model, null);

ArrayList<CLICSScoreboardRow>rowsArray = new ArrayList<CLICSScoreboardRow>();
HashMap<String, String> probEleToShort = new HashMap<String, String>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I read the name of this variable (and I've seen it in other classes as well), I immediately interpreted it as representing a HashMap which maps problem elementIds (probEle) to (java type) short. In fact, it's mapping ElementIds to problem short names. For clarity, I would suggest naming the variable (something like) probEleToShortName.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you think it's confusing, I changed it. Doesn't bother me either way. Changed in next push.

// list of xml entries for each team's standing
List<TeamStanding> standings = contestStandings.getTeamStandings();
if(standings != null) {
for (TeamStanding teamStanding : contestStandings.getTeamStandings()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is contestStandings.getTeamStandings() called here again, when the code two lines earlier created variable standings which contains that value?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it was a mistake. Just uses standings now. Next push it's fixed.

if(standings != null) {
for (TeamStanding teamStanding : contestStandings.getTeamStandings()) {
rowsArray.add(new CLICSScoreboardRow(probEleToShort, teamStanding));
rows = rowsArray.toArray(new CLICSScoreboardRow[0]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why rowsArray is being converted (and assigned to rows inside the for loop. It seems like this conversion should happen (once) after the for loop finishes...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, neither do I. I think I grabbed the essence of this code from somewhere else. (I forget where). And, it lost something in the translation to this module. I can tell it was "stolen" from somewhere else because the indenting was wrong too. (2 spaces instead of 4) Fixed in next push.

ObjectMapper mapper = JSONUtilities.getObjectMapper();
return mapper.writeValueAsString(this);
} catch (Exception e) {
return "Error creating JSON for scoreboard info " + e.getMessage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSScoreboardRow.java. Several comments, no showstoppers.

Comment on lines 14 to 15
* CLICS Scoreboard row
* Contains information about a single entry in the scoreboard
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaDoc...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

/**
* Fill in API scoreboard row information properties (for scoreboard endpoint)
*
* @param probEleToShort hashmap for mapping problem elementid to shortname
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted elsewhere, I think probEletoShort is confusing (with respect to Java type short); I'd recommend changing the name to probEleToShortName.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSTeam.java. Made several comments; no show-stoppers.

import edu.csus.ecs.pc2.services.core.JSONUtilities;

/**
* CLICS Team object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaDoc...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

ObjectMapper mapper = JSONUtilities.getObjectMapper();
return mapper.writeValueAsString(this);
} catch (Exception e) {
return "Error creating JSON for team info " + e.getMessage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to error message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

ObjectMapper mapper = new ObjectMapper();
newaccounts = createTeamsFromTeamsJSON(contest, mapper.readValue(jsonfile, CLICSTeam[].class), site, institutionsMap, log);
} catch (Exception e) {
log.log(Log.WARNING, "could not deserialize team file " + jsonfile, e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

ObjectMapper mapper = new ObjectMapper();
newaccounts = createTeamsFromTeamsJSON(contest, mapper.readValue(json, CLICSTeam[].class), site, institutionsMap, log);
} catch (Exception e) {
log.log(Log.WARNING, "could not deserialize team string", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "CLICS" to message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

}
} catch(Exception e) {
// Some sort of conversion error - log it and abort
log.log(Log.SEVERE, "unable to get team number from label or id", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest adding "CLICS" in front of "label" and "id".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/CLICSVerionInfo.java. Request changing class name to fix typo.

* @author John Buck
*
*/
public class CLICSVerionInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name of class is misspelled (Verion -> Version).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see who wrote this module originally - haha ?

 * @author Douglas A. Lane <[email protected]>
 * @author John Buck

Doug certainly did this type of thing A LOT. :-)
Fixd in nxt psh.

ObjectMapper mapper = JSONUtilities.getObjectMapper();
return mapper.writeValueAsString(this);
} catch (Exception e) {
return "Error creating JSON for version info " + e.getMessage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest adding "CLICS" to error message.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/ClarificationService.java. Have some substantial concerns about whether the synchronize clause on the POST method is going to cause problems. Have suggested at least a partial workaround.

*
* @param sc security info for the user making the request
* @param contestId Contest for which info is requested
* @return a {@link Response} object containing the contest languages in JSON form
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy/paste error? contest languages --> contest clarifications...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Fixed in next push.

return Response.status(Response.Status.NOT_FOUND).build();
}

// get the groups from the contest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

groups --> clarifications ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Fixed in next push.

* @param sc security info for the user making the request
* @param contestId Contest for which info is requested
* @param clarificationId the id of the desired clarification
* @return a {@link Response} object containing the contest languages in JSON form
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line doesn't describe what the method returns (it returns a single clarification, not a language).

if(contestId.equals(model.getContestIdentifier()) == false) {
return Response.status(Response.Status.NOT_FOUND).build();
}
// get the groups from the contest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops... copy/paste: groups --> clarifications.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Fixed in next push.

// be set to non-null
Clarification clarNoAnswer = null;

// create list of clarifications to send back
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is a bit misleading, because the method isn't supposed to return a LIST -- just ONE clarification.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I already fixed that before you found it. Didn't push it though. Will be pushed now. Also, made the comment a bit better indicating that it first checks for an answer, and if not found, it checks for the clarification itself.

return Response.status(Status.BAD_REQUEST).entity("must include time property").build();
}

controller.getLog().log(Level.WARNING, "Admin PUT clarification is not implemented due to forcing the ID");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what ...due to forcing the ID means. Could the message explain slightly more?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's clear. We can't force ID's in PC2.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I guess it still seems vague to me. Are you talking about the fact that PC2 can't support the ability of an external client to set the id of an existing clar?

*
* @param contestId contest identifier
* @param jsonRequestString
* a JSON string specifying a starttime request in CLICS format
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why this method has anything do to specifically with a starttime request... Copy/Paste?

And actually, it's not clear that this method is ever used anywhere...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted the method. Not used. Left over from copy. Next push.

}
}
}
return(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this statement is intended to be return(ret) ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's a bug. Fixed in next push.

/**
* Try to find the Clarification from its id
*
* @param szClarId This ID of the clar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter name here (szClarId) doesn't make sense... nor does the word This...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.


Clarification clar = null;

// create list of clarifications to send back
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't quite make sense -- there is no "list of clarifications being sent back". I think it's supposed to say (something like) create list of clarfications to be checked ? Although in fact no "list" is actually created at all; it's just fetched from the model...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

Mostly fixes to comments to correct misspellings.
Some nomenclature changes for variables.
Fixed CLICSVersionInfo class name spelling error.
Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/ContestService.java. Mostly innocuous comments; no show-stoppers. I did express concern about two things marked as "TODOs" that we might need as Primary - so maybe we need to file Issues on them?

final Map<String, String> jsonDataMap;

try {
jsonDataMap = mapper.readValue(jsonRequestString, mapType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... I would have thought that since jsonDataMap is declared final, it's not allowed to assign a value to it here. No?

(I do see that it is final but it hasn't been "initialized"; I assume that has something to do with it. But I've never seen that construct in Java. I assume it works?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this sheds some light on it. Of course, local variables must be initialized before used, or you will get an error.


// if the map is null then the parsing failed
if (requestMap == null) {
controller.getLog().log(Log.WARNING, LOG_PREFIX + contestId + ": unable to parse JSON starttime string");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't appear to me that at this point we know that the request was a starttime patch -- only that it failed to parse correctly. (It appears to me that it could, for example, have been a (failed) scoreboard_thaw_time patch.)
If I'm right about that then the above message is misleading...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Fixed in next push.

if (requestMap == null) {
controller.getLog().log(Log.WARNING, LOG_PREFIX + contestId + ": unable to parse JSON starttime string");
// return HTTP 400 response code per CLICS spec
return Response.status(Status.BAD_REQUEST).entity("Bad JSON starttime request").build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment: how do we know this was a (failed) starttime PATCH (as opposed to some other (failed) PATCH)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Fixed in next push.

return Response.status(Status.BAD_REQUEST).entity("Bad JSON starttime request").build();
}

// if we get here then the JSON parsed correctly; see if it contained "starttime" as a key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is misleading; the following lines of code are checking for the existence of CONTEST_ID_KEY, not "starttime".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Fixed in next push.

// Flag to indicate that countdown pause time was specified
boolean sawCountdownPauseTime = false;

// if we get here then the JSON parsed correctly; see if it contained "start_time" as a key (that is required by spec)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm misinterpreting the spec, but it appears to me that start_time is NOT "required" (as stated by the above comment -- rather, a PATCH could instead consist of (just) id and scoreboard_thaw_time. If I'm right, that means the above comment isn't exactly correct... "start_time" isn't required. (Indeed, the following code appears to correctly check for scoreboard_thaw_time if start_time is missing... my point is that the comment explicitly (incorrectly) states that start_time is required.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. I clarified the test for CONTEST_THAW_TIME as well. Fixed in next push.

GregorianCalendar thawTime = getDate(contestId, thawTimeValue);
if (thawTime != null) {
// Set thaw time to this time.
// TODO: tell PC2 to thaw the contest at the given time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THIS todo I think we're ok without for Primary. The two other ones (noted elsewhere), I worry we might need...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Comment on lines 526 to 528
* This method returns a representation of the current contest scheduled start time in JSON format as described on the CLICS wiki.
*
* @return a {@link Response} object containing a JSON String giving the scheduled contest start time as a Unix Epoch value, or as the string "undefined" if no start time is currently scheduled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments refer to "scheduled start time", but I don't see that this method has anything to do with that...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in next push.

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getContests() {
CLICSContestInfo [] allContests = new CLICSContestInfo[1];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I correct that this array is size[1] because, while we're pretending that we're implementing a full CLICS "multi-contest" API, in fact PC2 only handles a single contest?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I fixed the comment to explain that. Fixed in next push.

}

/**
* Check the user has a role than change contest start time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird syntax in this comment...

}

/**
* Check the user has a role than change contest thaw time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto - weird syntax in comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh my, yes. I have no idea where that wording came from. Gibberish. I also added the following TODO: to the comment:

    /**
     * Check that the user's role permits setting contest thaw time
     * TODO: Should we always return false here since we don't implement this?
     *
     * @param sc Security context for the user
     * @return true if the user can perform the operation
     */

Fixed in next push.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved one comment.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-reviewed src/edu/csus/ecs/pc2/clics/API202306/ClarificationService.java. Resolved a number of comments; still have a couple that are awaiting a further response.

Comment on lines +167 to +168
clarAnswers = clarification.getClarificationAnswers();
if (clarAnswers != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think this another case where I didn't explain myself properly/sufficiently. The code in question is:

clarAnswers = clarification.getClarificationAnswers();
if (clarAnswers != null) {
   for (ClarificationAnswer clarAns: clarAnswers) {
        if (clarAns.getElementId().toString().equals(clarificationId)) {
            try {
                 //map the found clar into JSON and return it, or else return a JSON error...
             }
           ...
       }
    }
 }

I believe what this code is trying to do is:

  get all answers to the specified clarification;
  if (there are some answers) {
    look through the answers to see if an answer matches the specified clarification;
    if a match is found, return it;

If I've misunderstood the intent of the code as described above, never mind. But if I'm correct, then my point is that the test if (there are some answers) is implemented in the code as if (clarAnswers != null). In other words, "if the call to clarification.getClarificationAnswers returns something other than null, execute the for loop which searches answers for a matching id".

However, in looking at the code in method Clarification.getClarificationAnswers(), it appears to me that it will ALWAYS return something other than null. I say this because the single line of code in that method is

return answerList.toArray(new ClarificationAnswer[answerList.size()]);

The variable answerList is declared and initialized with the following:

private ArrayList<ClarificationAnswer> answerList = new ArrayList<ClarificationAnswer>();

This means that answerList is an empty ArrayList to start with -- and that's what it'll still be if there no answers added to the answerList. Thus, answerList.toArray(new ClarificationAnswer[answerList.size()]); will always return a reference to an array (which will have size=0 if there are no answers). It will never return null.

Therefore, the test

                clarAnswers = clarification.getClarificationAnswers();
                if (clarAnswers != null) {

will always be true -- even if there are no answers. So the code enters the for loop looking for a matching answer -- but there are no answers.

It doesn't make sense to me why the code enters a loop to process answers if the condition is "there are no answers". That's what I was saying. It seems like the test if (clarAnswers != null) should actually be

                clarAnswers = clarification.getClarificationAnswers();
                if (clarAnswers != null && clarAnswers.length>0) {

return Response.status(Status.BAD_REQUEST).entity("must include time property").build();
}

controller.getLog().log(Level.WARNING, "Admin PUT clarification is not implemented due to forcing the ID");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I guess it still seems vague to me. Are you talking about the fact that PC2 can't support the ability of an external client to set the id of an existing clar?

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/EventFeedService.java. No major issues, although as I noted in one comment I'm very unclear on where the properties of an event are formally specified...

public static String createEventFeedJSON(IInternalContest contest, IInternalController controller, HttpServletRequest servletRequest, SecurityContext sc) {
EventFeedStreamer streamer = new EventFeedStreamer(contest, controller, servletRequest, sc);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
streamer.addStream(stream, new EventFeedFilter());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if there was a comment noting that addStream() also outputs all past events to the stream (just to help the reader...) Otherwise, people (like me) will spend time trying to figure out why there are two consecutive statements

streamer.addStream(stream, new EventFeedFilter());
streamer.removeStream(stream);

Without knowing that side-effect of addStream(), that sequence makes little sense...

(And yes, I'm aware that this is legacy code...)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Fixed in next push.

* @return CLICSEndpoint object if the user can access this endpoint's properties, null otherwise
*/
public static CLICSEndpoint getEndpointProperties(SecurityContext sc) {
String [] efProps = { "type", "id", "data", "token" };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems strange that these efProps are defined here as hard-coded strings; I would expect them to be defined as constants (somewhere). Also, I can't find any definition of these properties anywhere in the CLICS spec (some -- but not all -- of them appear in the Examples section under the Event feed endpoint description, but I can't find anywhere that they are actually defined/listed in the spec). (This of course is a problem with the spec, not with this code... but still I'm trying to figure out the source of their definition...)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I answered this in the slack chat, but I will duplicate that response here (without my editorial) for posterity:

The 2023-06 spec has it buried like other things in the spec. See "General Design Principles"->Notification Format. https://ccs-specs.icpc.io/2023-06/contest_api?highlight=num_solved#notification-format

I'm OK with having these as string literals in the array since they are specific to the protocol being implemented, and, are local to this module.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/EventFeedLog.java. One minor comment; no issues.

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

/**
* Event fee log.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know there was a fee for the events... :)

(And yes, I know this is legacy code... ;)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if Doug was collecting a fee on the side for anyone who used the EF? :-)

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/EventFeedOperation.java. Other than the fact that this class isn't actually referenced anywhere in PC2 (and therefore maybe should be deleted), no problems.

*
* @author Douglas A. Lane, PC^2 Team, [email protected]
*/
public enum EventFeedOperation {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not appear to me that this class (enum) is referenced anywhere in the PC2 project. Perhaps it should be deleted?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed it. It is legacy. the "op" property is no longer part of the notification format, which is what this class was used for.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/EventFeedType.java. Left a couple of comments, some of which may be due to my lack of detailed understanding of how the Event Feed works...

Comment on lines 23 to 27
CONTESTS("contests"),
/**
*
*/
CONTEST("contest"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why we have both a CONTEST and a CONTESTS type. In addition, I can't find any place in the CLICS spec that defines either of these event types (am I misreading the spec, or is this another of those places where the spec is, uh, "incomplete" ?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These event types are for filtering on the event type field in a notification. Notification format description. Note that although the endpoint is called "/contests/" the notification type is "contest" in the EF sigh I kind of understand why... someone is asking about a specific contest by supplying the contest ID. Granted, that CONTESTS ("contests") should never be a type in the event feed for 2023-06, however, this is what it was called for 2020-03. I guess I carried it over and left it there since it wasn't harmful. Although, it may be misleading because we'll accept that as a valid type filter, but it will never allow any records to be sent on the feed since no notification should have a type of "contests". Removing it, would cause an IAE if someone supplied it. I think this may require a little more analysis to confirm what I just said, but I'm about 90% sure of it. I'll leave it for now.

/**
*
*/
AWARDS("awards"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't there be an ACCOUNTS type here (or somewhere in the enum)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Accounts are not sent on the EF. See Notification format description

/**
*
*/
SUBMISSIONS("submissions"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't there be a SCOREBOARD type here (or somewhere in the enum)? Or do changes in the scoreboard not generate events?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Correct. See the spec notifications description as mentioned above.

Copy link
Contributor

@clevengr clevengr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed src/edu/csus/ecs/pc2/clics/API202306/EventFeedStreamer.java. I left a couple of comments, but to be honest I am not completely sure how this class works (in conjunction with the related event fedd classes) so I don't really feel qualified to review it thoroughly...

*
* @author Douglas A. Lane, PC^2 Team, [email protected]
*/
public class EventFeedStreamer extends JSON202306Utilities implements Runnable, UIPlugin, IEventFeedStreamer {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It surprises me to see that this class implements... UIPlugin. I can't imagine where this class could plug in to a UI. (But maybe there's a reason. And yes, I realize it's been this way since, well, Doug :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing it may have had something to do with setContestAndController(), since UIPlugin does that. But, it seems any method that needs a contest or controller has it passed in. It may truly be legacy.


private JudgementListener judgementListener = new JudgementListener();

private ContestInformationListener contestInformationListener = new ContestInformationListener();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that the ContestInformationListener handles generating events for type CONTEST and for type STATE. I also see that there are listeners to handle events of type ACCOUNTS (teams), SUBMISSIONS (the RunListener), CLARIFICATIONS, PROBLEMS, LANGUAGES, GROUPS, and JUDGEMENTS. What's not clear to me is how events for JUDGEMENT_TYPES and ORGANIZATIONS are generated.

This may be just more of my lack of understanding of how the event feed works...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See JudgementListener in EventFeedStreamer around line 720. There is currently no way (that I am aware of) to add an organization to a contest that has been configured already. (IE no IOrganizationListener). Organizations are added during configuration only. Organizations events are only generated once when the EF is started and that's it. When organizations (institutions) are added the correct way and given their own model I suppose we'll add a listener for them then, but BFTF...

Various comment fixes and clarifications.
Removed unused EventFeedOperation Class.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLICS CCS Update Changes to update to the CLICS specification Comments/Analysis Requested Soliciting comments and feedback from the Team about how to implement issue In PR code review Primary CCS Required for PC2 to act as primary CCS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants