-
Notifications
You must be signed in to change notification settings - Fork 26
I1179 create zip for submit + CI's #1180
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
|
|
||
| import java.io.IOException; | ||
| import java.text.ParseException; | ||
| import java.util.Date; | ||
| import java.util.GregorianCalendar; | ||
| import java.util.Map; | ||
|
|
||
|
|
@@ -420,11 +421,22 @@ private Response HandleContestThawTime(SecurityContext sc, String contestId, Str | |
| // thaw time present, validate now | ||
| 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. | ||
| controller.getLog().log(Log.WARNING, LOG_PREFIX + contestId + ": setting of contest thaw time is not implemented"); | ||
| return Response.status(Status.NOT_MODIFIED).entity("Unable to set contest thaw time to " + thawTime.toString()).build(); | ||
| Date thawDate = thawTime.getTime(); | ||
| String thawStr = Utilities.getIso8601formatterWithMS().format(thawDate); | ||
| // get the local model's ContestInformation sincd we are modifying the thaw time | ||
| ContestInformation ci = model.getContestInformation(); | ||
| if (ci != null) { | ||
| // set the new start date/time into the ContestInformation | ||
| ci.setThawed(thawDate); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The preceding comment says we are setting the "new start date/time", but I don't think that's what's actually being set here. (Copy/paste without an update?) |
||
| // tell the Controller to update the ContestInformation, eg the thaw time in this case | ||
| controller.updateContestInformation(ci); | ||
| controller.getLog().log(Log.INFO, LOG_PREFIX + contestId + ": setting contest thaw time to " + thawStr); | ||
| return Response.ok().entity("Contest thaw time set to " + thawStr).build(); | ||
| } | ||
| controller.getLog().log(Log.WARNING, LOG_PREFIX + contestId + ": can not get contest information to set thaw time"); | ||
| return Response.status(Status.NOT_MODIFIED).entity("Unable to set contest thaw time to " + thawStr).build(); | ||
| } | ||
| controller.getLog().log(Log.WARNING, LOG_PREFIX + contestId + ": bad value for contest thaw time: " + thawTimeValue); | ||
| return Response.status(Status.BAD_REQUEST).entity("Bad value for contest thaw time request").build(); | ||
|
|
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,7 @@ | |
| import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; | ||
| import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; | ||
|
|
||
| import edu.csus.ecs.pc2.convert.EventFeedUtilities; | ||
| import edu.csus.ecs.pc2.core.IInternalController; | ||
| import edu.csus.ecs.pc2.core.Utilities; | ||
| import edu.csus.ecs.pc2.core.exception.SubmissionRejectedException; | ||
|
|
@@ -607,24 +608,44 @@ public synchronized Response addNewSubmission(@Context HttpServletRequest servle | |
| return Response.status(Response.Status.BAD_REQUEST).entity("no file specified").build(); | ||
| } | ||
|
|
||
| List<IFile> srcFiles = new ArrayList<IFile>(); | ||
| for(CLICSFileReference file : files) { | ||
| String fileName = file.getFilename(); | ||
| if("".equals(fileName)) { | ||
| return Response.status(Response.Status.BAD_REQUEST).entity("no file name specified").build(); | ||
| List<IFile> srcFiles; | ||
|
|
||
| // Backward compatability test: If no mime property specified on the first file, then the CLICSFileReference's are | ||
| // actual files and not a zip file. This is in here because initially, due to a misinterpretation of the CLICS | ||
| // 2023-06 specification, the command line submit utility (a.k.a. pc2submit) did not put the files in a zip file, | ||
| // rather, it created an array of base64 encoded file contents. The CLICS spec has since been clarified but for now, | ||
| // we will still support the incorrect implementation as well as the correct one. | ||
| CLICSFileReference firstFile = files[0]; | ||
| String mimeType = firstFile.getMime(); | ||
|
|
||
| // TODO: deprecate this "if" part and leave the "else" part. JB | ||
| if(mimeType == null || "".equals(mimeType)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A zip with an empty mime is also allowed according to the spec, I think that's not handled now?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you take a look at the comments in the PR, we are using the absence of the After the next contest image cycle, we will remove the deprecated code and use the absence of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense! |
||
| srcFiles = new ArrayList<IFile>(); | ||
| for(CLICSFileReference file : files) { | ||
| String fileName = file.getFilename(); | ||
| if("".equals(fileName)) { | ||
| return Response.status(Response.Status.BAD_REQUEST).entity("no file name specified").build(); | ||
| } | ||
| // allow contestant submission of a zero length file. This will generate a CE (hopefully). | ||
| // if the following code is uncommented, the submission is not made and a 400 is returned to the submitter. | ||
| // it appears that other CCS's allow zero length submissions. *sigh* -- JB | ||
| String fileData = file.getData(); | ||
| if(fileData == null || fileData.length() == 0) { | ||
| // nice to put it in the log in case any questions come up. | ||
| log.info(user + " POSTing empty source submission on behalf of team " + team_id); | ||
|
|
||
| // return Response.status(Response.Status.BAD_REQUEST).entity("no file data specified for " + fileName).build(); | ||
| } | ||
|
Comment on lines
+633
to
+638
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PR #1173 (handle zero-length files in WTI) includes support for PC2 Admin configuration of "Allow zero-length files?". Once that PR is approved and merged, the above block of code should probably check the |
||
| IFile iFile = new IFileImpl(file.getFilename(), fileData); | ||
| srcFiles.add(iFile); | ||
| } | ||
| // allow contestant submission of a zero length file. This will generate a CE (hopefully). | ||
| // if the following code is uncommented, the submission is not made and a 400 is returned to the submitter. | ||
| // it appears that other CCS's allow zero length submissions. *sigh* -- JB | ||
| String fileData = file.getData(); | ||
| if(fileData == null || fileData.length() == 0) { | ||
| // nice to put it in the log in case any questions come up. | ||
| log.info(user + " POSTing empty source submission on behalf of team " + team_id); | ||
|
|
||
| // return Response.status(Response.Status.BAD_REQUEST).entity("no file data specified for " + fileName).build(); | ||
| } else { | ||
| // There should be precisely one file in the files[] array, which is a zip archive of the source file(s) | ||
| if(files.length > 1) { | ||
| return Response.status(Response.Status.BAD_REQUEST).entity("only one zip archive is allowed").build(); | ||
| } | ||
| IFile iFile = new IFileImpl(file.getFilename(), fileData); | ||
| srcFiles.add(iFile); | ||
|
|
||
| srcFiles = EventFeedUtilities.getIFiles(firstFile.getData()); | ||
| } | ||
| String entry = sub.getEntry_point(); | ||
| IFile mainFile = srcFiles.get(0); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,31 @@ | ||
| // Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. | ||
| // Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. | ||
| package edu.csus.ecs.pc2.convert; | ||
|
|
||
| import java.io.ByteArrayInputStream; | ||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.File; | ||
| import java.util.ArrayList; | ||
| import java.util.Base64; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.zip.ZipEntry; | ||
| import java.util.zip.ZipInputStream; | ||
|
|
||
| import edu.csus.ecs.pc2.core.model.IFile; | ||
| import edu.csus.ecs.pc2.core.model.IFileImpl; | ||
|
|
||
| /** | ||
| * Event Feed Utilities | ||
| * | ||
| * | ||
| * @author ICPC | ||
| * | ||
| */ | ||
| public final class EventFeedUtilities { | ||
|
|
||
| public static final long MS_PER_SECOND = 1000; | ||
|
|
||
| private EventFeedUtilities() { | ||
| super(); | ||
| } | ||
|
|
@@ -29,7 +37,7 @@ public static String[] getAllLanguages(List<EventFeedRun> runs) { | |
| map.put(eventFeedRun.getLanguage(), ""); | ||
| } | ||
| Set<String> set = map.keySet(); | ||
| return (String[]) set.toArray(new String[set.size()]); | ||
| return set.toArray(new String[set.size()]); | ||
| } | ||
|
|
||
| public static int getMaxProblem(List<EventFeedRun> runs) { | ||
|
|
@@ -55,7 +63,7 @@ public static int getMaxTeam(List<EventFeedRun> runs) { | |
|
|
||
| /** | ||
| * Convert decimal string to ms. | ||
| * | ||
| * | ||
| * @param decimalSeconds | ||
| * - declimal second | ||
| * @return ms | ||
|
|
@@ -98,7 +106,7 @@ public static long toMS(String decimalSeconds) { | |
|
|
||
| /** | ||
| * Fetch list of filenames, full path | ||
| * | ||
| * | ||
| * @param dirname | ||
| * location of submission files | ||
| * @param runId | ||
|
|
@@ -123,4 +131,88 @@ public static List<String> fetchRunFileNames(String dirname, String runId) { | |
| return list; | ||
| } | ||
|
|
||
| /** | ||
| * Get files from a zipfile's base64 encoded string data | ||
| * | ||
| * @param base64Data String comprising a zip file encoded as base64 | ||
| * @return list of IFiles extracted from the input bytes | ||
| * @throws IllegalArgumentException from the base64 decoder on a data error | ||
| */ | ||
| public static List<IFile> getIFiles(String base64Data) { | ||
|
|
||
| // use the decoder to both check the validity of, and to store, the byte data. | ||
| // this will throw an IllegalArgumentException if the data is basd | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo in comment? ("basd"?) |
||
| return(getIFiles(Base64.getDecoder().decode(base64Data))); | ||
| } | ||
|
|
||
| /** | ||
| * Get files from a zipfile's bytes. | ||
| * | ||
| * @param bytes bytes comprising a zip file. | ||
| * @return list of IFiles extracted from the input bytes | ||
| */ | ||
| public static List<IFile> getIFiles(byte[] bytes) { | ||
|
|
||
| List<IFile> files = new ArrayList<IFile>(); | ||
|
|
||
| ZipInputStream zipStream = null; | ||
|
|
||
| try { | ||
| zipStream = new ZipInputStream(new ByteArrayInputStream(bytes)); | ||
| ZipEntry entry = null; | ||
| /** | ||
| * Read each zip entry, add IFile. | ||
| */ | ||
| while ((entry = zipStream.getNextEntry()) != null) { | ||
|
|
||
| String entryName = entry.getName(); | ||
|
|
||
| // ByteOutputStream byteOutputStream = new ByteOutputStream(); | ||
| ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); | ||
|
|
||
| byte[] buffer = new byte[8096]; | ||
| int bytesRead = 0; | ||
| while ((bytesRead = zipStream.read(buffer)) != -1) | ||
| { | ||
| byteOutputStream.write(buffer, 0, bytesRead); | ||
| } | ||
|
|
||
| // String base64Data = getBase64Data(byteOutputStream.getBytes()); | ||
| String base64Data = getBase64Data(byteOutputStream.toByteArray()); | ||
| IFile iFile = new IFileImpl(entryName, base64Data); | ||
| files.add(iFile); | ||
|
|
||
| byteOutputStream.close(); | ||
|
|
||
| zipStream.closeEntry(); | ||
| } | ||
| zipStream.close(); | ||
|
|
||
| } catch (Exception e) { | ||
| if (zipStream != null){ | ||
| try { | ||
| zipStream.close(); | ||
| } catch (Exception ze) { | ||
| ; // problem closing stream, ignore. | ||
| } | ||
| } | ||
| throw new RuntimeException(e); | ||
| } | ||
|
|
||
| return files; | ||
|
|
||
| } | ||
|
|
||
| /** | ||
| * Encode bytes into BASE64. | ||
| * @param data | ||
| * @return | ||
| */ | ||
| public static String getBase64Data( byte [] bytes) { | ||
| // TODO REFACTOR move to FileUtilities | ||
| Base64.Encoder encoder = Base64.getEncoder(); | ||
| String base64String = encoder.encodeToString(bytes); | ||
| return base64String; | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(trivial) typo: "sincd" -> "since"