All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.faylixe.googlecodejam.client.CodeJamSession Maven / Gradle / Ivy

There is a newer version: 1.4.11
Show newest version
package fr.faylixe.googlecodejam.client;

import static fr.faylixe.googlecodejam.client.executor.Request.*;
import fr.faylixe.googlecodejam.client.common.NamedObject;
import fr.faylixe.googlecodejam.client.common.Resources;
import fr.faylixe.googlecodejam.client.executor.HttpRequestExecutor;
import fr.faylixe.googlecodejam.client.webservice.ContestInfo;
import fr.faylixe.googlecodejam.client.webservice.InitialValues;
import fr.faylixe.googlecodejam.client.webservice.Problem;
import fr.faylixe.googlecodejam.client.webservice.ProblemInput;
import fr.faylixe.googlecodejam.client.webservice.SubmitResponse;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.api.client.http.HttpMediaType;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.MultipartContent;
import com.google.gson.Gson;

/**
 * {@link CodeJamSession} is the main API entry point, which consists
 * in logging to a code jam platform ``hostname`` and then providing
 * facilities such as :
 * 
* * Retrieves contest, round * * Interacts with platform API from a given round * * Downloads input * * Submits solutions * * @author fv */ public final class CodeJamSession extends NamedObject implements Serializable { /**

Serialization index.

**/ private static final long serialVersionUID = 1L; /**

Downloaded input file extension used for filename generation.

**/ private static final String INPUT_EXTENSION = ".in"; /**

Practice file type for unactive contest.

**/ private static final String PRACTICE = "practice"; /**

Error message set when analysis retrieval failed.

**/ private static final String ANALYSIS_ERROR = "An error occurs while retrieving analysis : %s"; /**

Character used for file name generation.

**/ private static final char FILENAME_SEPARATOR = '-'; /**

Logged HTTP executor for executing queries.

**/ private final HttpRequestExecutor executor; /**

Current selected round this session is working on.

**/ private final Round round; /**

Current contest info this session is working on.

**/ private final ContestInfo info; /**

Initial values this session is working on.

**/ private final InitialValues values; /**

Map of already loaded contest analysis.

**/ private final Map analysis; /** *

Static factory that builds the name of this session * regarding of the given round.

* * @param round Round to build name from. * @return Name built. */ private static String buildContestName(final Round round) { final String contestName = Resources.normalize(round.getContestName()); final String roundName = Resources.normalize(round.getName()); return new StringBuilder(contestName.toLowerCase()) .append('-') .append(roundName.toLowerCase()) .toString(); } /** *

Default constructor.

* * @param executor Logged HTTP executor for executing queries. * @param round Current selected round this session is working on. * @param info Current contest info this session is working on. * @param values Initial values this session is working on. */ private CodeJamSession( final HttpRequestExecutor executor, final Round round, final ContestInfo info, final InitialValues values) { super(buildContestName(round)); this.executor = executor; this.round = round; this.info = info; this.values = values; this.analysis = new HashMap<>(); } /** *

Reloads session components in order to prevent from any change.

* * @return A newly created session updated otherwise. * @throws IOException If any error occurs while reloading a new session. */ public CodeJamSession refresh() throws IOException { return createSession(executor, round); } /** *

Performs and returns a GET / request * in order to get all round detail.

* * @return Request response as a {@link ContestInfo} POJO. */ public ContestInfo getContestInfo() { return info; } /** *

Indicates if the currently logged user is qualified * for the next round or not.

* * @return true if user is qualified, false otherwise. */ public boolean isQualified() { return values.isQualified(); } /** *

Indicates if the current session is logged in or not.

* * @return true if user is logged, false otherwise. */ public boolean isLogged() { return values.isLogged(); } /** *

Retrieves the problem associated * to the given letter.

* * @param letter Letter that identifies the problem. * @return Corresponding problem if exist, null otherwise. */ public Problem getProblem(final String letter) { if (letter.isEmpty() || letter.length() > 1) { return null; } final char identifier = letter.toUpperCase().charAt(0); final int index = (int) identifier - (int) 'A'; final List problems = info.getProblems(); if (index < 0 || index >= problems.size()) { return null; } return problems.get(index); } /** *

Indiciates if the contest is currently active, * namely if competition is occuring at the current * time, or not.

* * @return true if the contest is active, false otherwise. */ public boolean isActive() { final long now = System.currentTimeMillis(); final long start = values.getStart(); final long end = start + values.getLeft(); // TODO : Ensure predicate consistency. return now >= start && now <= end; } /** *

Retrieves and returns the analysis * for the given problem.

* * @param problem Problem to retrieve analysis from. * @return Analysis if any. * @throws IOException If any error occurs while retrieving analysis. */ private String getContestAnalysis(final String problemId) throws IOException { final StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(round.getURL()) .append(DO) .append(COMMAND_PARAMETER) .append(ANALYSIS_COMMAND) .append(PROBLEM_PARAMETER) .append(problemId) .append(CSRF_PARAMETER) .append(values.getToken()) .append(AGENT_PARAMETER) .append(DEFAULT_AGENT); return executor.get(urlBuilder.toString()); } /** *

Returns the analysis for the given * problem if any.

* * @param problem Problem to retrieve analysis from. * @return Analysis if any. */ public String getContestAnalysis(final Problem problem) { if (!analysis.containsKey(problem)) { try { final String content = getContestAnalysis(problem.getId()); analysis.put(problem, content); } catch (final IOException e) { return String.format(ANALYSIS_ERROR, e.getMessage()); } } return analysis.get(problem); } /** *

Builds and returns a valid file name * for the given problem input.

* * @param input Input to retrieve file name from. * @param attempt Attempt number. * @return Built file name. */ public String buildFilename(final ProblemInput input, final int attempt) { final StringBuilder builder = new StringBuilder(); final Problem problem = input.getProblem(); final ContestInfo info = problem.getParent(); final int index = info.getProblems().indexOf(problem); final char letter = (char) ((int) 'A' + index); builder.append(letter) .append(FILENAME_SEPARATOR) .append(input.getName()) .append(FILENAME_SEPARATOR); if (attempt == -1) { builder.append(PRACTICE); } else { builder.append(attempt); } builder.append(INPUT_EXTENSION); return builder.toString(); } /** *

Downloads and returns the stream of the * input file associated to the given problem * input in a practice mode.

* * @param input Input to download file from. * @return Stream to read which contains our downloaded file data. * @throws IOException If any error occurs while downloading the file. */ public InputStream download(final ProblemInput input) throws IOException { return download(input, -1); } /** *

Downloads and returns the stream of the * input file associated to the given problem * input.

* * @param input Input to download file from. * @param attempt Attempt number. * @return Stream to read which contains our downloaded file data. * @throws IOException If any error occurs while downloading the file. */ public InputStream download(final ProblemInput input, final int attempt) throws IOException { final String filename = buildFilename(input, attempt); final StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(round.getURL()) .append(DO) .append(COMMAND_PARAMETER) .append(DOWNLOAD_COMMAND) .append(PROBLEM_PARAMETER) .append(input.getProblem().getId()) .append(FILENAME_PARAMETER) .append(filename) .append(INPUT_ID_PARAMETER) .append(input.getNumber()) .append(CSRF_PARAMETER) .append(values.getURLEncodedToken()) .append(AGENT_PARAMETER) .append(DEFAULT_AGENT); final HttpRequest request = executor.getRequest(urlBuilder.toString()); final HttpResponse response = request.execute(); return response.getContent(); } /** *

Submits the given output file and the * given source file for the given problem * input. This method should be call only * after a successful call to the {@link #download(ProblemInput)} * method on the same input, as the evaluation * system will judge the last downloaded dataset * based on the internal token / session.

* * @param input Input file to submit solution for. * @param output Output file produced by the algorithm. * @param source Source code file of the algorithm to submit. * @return Request response, as a {@link SubmitResponse} instance. * @throws IOException If any error occurs while uploading data, or performing the request. */ public SubmitResponse submit(final ProblemInput input, final File output, final File source) throws IOException { final MultipartContent content = createContent(input, output, source); final StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(round.getURL()); urlBuilder.append(DO); final String response = executor.post(urlBuilder.toString(), content); final Gson gson = new Gson(); return gson.fromJson(response, SubmitResponse.class); } /** *

Created and returns a valid {@link MultipartContent} instance * that contains data required for submission.

* * @param input Input file to submit solution for. * @param output Output file produced by the algorithm. * @param source Source code file of the algorithm to submit. * @return Created multipart content. */ private MultipartContent createContent(final ProblemInput input, final File output, final File source) throws IOException { final HttpMediaType type = new HttpMediaType(MEDIA_TYPE); type.setParameter(BOUNDARY, createBoundary()); final MultipartContent content = new MultipartContent() .setMediaType(type) .addPart(HttpRequestExecutor.buildDataPart(CSRF_PARAMETER_NAME, values.getToken())) .addPart(HttpRequestExecutor.buildFilePart(ANSWER_PARAMETER, output)) .addPart(HttpRequestExecutor.buildFilePart(SOURCE_FILE_PARAMETER, source)) .addPart(HttpRequestExecutor.buildDataPart(COMMAND_PARAMETER_NAME, SUBMIT_COMMAND)) .addPart(HttpRequestExecutor.buildDataPart(PROBLEM_PARAMETER_NAME, input.getProblem().getId())) .addPart(HttpRequestExecutor.buildDataPart(INPUT_ID_PARAMETER_NAME, String.valueOf(input.getNumber()))) .addPart(HttpRequestExecutor.buildDataPart(NUM_SOURCE_FILE_PARAMETER, DEFAULT_NUM_SOURCE_FILE)) .addPart(HttpRequestExecutor.buildDataPart(AGENT_PARAMETER_NAME, DEFAULT_AGENT)); return content; } /** *

Static factory method that should be used for creating a session. * Loads associated contest info and initial values from the given * round, using the given executor.

* * @param executor {@link HttpRequestExecutor} instance to use. * @param round Contextual {@link Round} instance this session is bound to. * @return Created session. * @throws IOException If any error occurs while retrieving contest info or initial values. */ public static CodeJamSession createSession(final HttpRequestExecutor executor, final Round round) throws IOException { final ContestInfo info = ContestInfo.get(executor, round); final InitialValues values = InitialValues.get(executor, round); return new CodeJamSession(executor, round, info, values); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy