diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java index a8b9f078e35e..6926cb379654 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java @@ -10,6 +10,7 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.hash.HashMap; import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.TaskFile; @@ -29,6 +30,10 @@ public class StudySerializationUtils { public static final String PLACEHOLDERS = "placeholders"; public static final String LINE = "line"; public static final String START = "start"; + public static final String LENGTH = "length"; + public static final String POSSIBLE_ANSWER = "possible_answer"; + public static final String HINT = "hint"; + public static final String HINTS = "hints"; public static final String OFFSET = "offset"; public static final String TEXT = "text"; public static final String LESSONS = "lessons"; @@ -378,6 +383,7 @@ public class StudySerializationUtils { @Override public TaskFile deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); JsonObject taskFileObject = json.getAsJsonObject(); JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS); for (JsonElement placeholder : placeholders) { @@ -390,8 +396,46 @@ public class StudySerializationUtils { Document document = EditorFactory.getInstance().createDocument(taskFileObject.getAsJsonPrimitive(TEXT).getAsString()); placeholderObject.addProperty(OFFSET, document.getLineStartOffset(line) + start); } + final String hintString = placeholderObject.getAsJsonPrimitive(HINT).getAsString(); + final JsonArray hintsArray = new JsonArray(); + + try { + final List hints = gson.fromJson(hintString, List.class); + for (String hint : hints) { + hintsArray.add(hint); + } + } + catch (JsonParseException e) { + hintsArray.add(hintString); + } + placeholderObject.add(HINTS, hintsArray); } - return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create().fromJson(json, TaskFile.class); + + return gson.fromJson(json, TaskFile.class); + } + } + + public static class StepicAnswerPlaceholderAdapter implements JsonSerializer { + @Override + public JsonElement serialize(AnswerPlaceholder src, Type typeOfSrc, JsonSerializationContext context) { + final List hints = src.getHints(); + + final int length = src.getLength(); + final int start = src.getOffset(); + final String possibleAnswer = src.getPossibleAnswer(); + int line = -1; + + final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + final JsonObject answerPlaceholder = new JsonObject(); + answerPlaceholder.addProperty(LINE, line); + answerPlaceholder.addProperty(START, start); + answerPlaceholder.addProperty(LENGTH, length); + answerPlaceholder.addProperty(POSSIBLE_ANSWER, possibleAnswer); + + final String jsonHints = gson.toJson(hints); + answerPlaceholder.addProperty(HINT, jsonHints); + + return answerPlaceholder; } } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java index 7e96ab0011ea..d2353eed4570 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java @@ -15,6 +15,7 @@ import java.util.List; public class AnswerPlaceholder { + @SerializedName("hints") @Expose private List myHints = new ArrayList(); @SerializedName("possible_answer") diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java index 56765a78f1ce..02770f418288 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java @@ -11,7 +11,7 @@ import java.util.ArrayList; import java.util.List; public class Lesson implements StudyItem { - @Expose private int myId; + @Expose @SerializedName("id") private int myId; @Transient public List steps; @Transient public List tags; @Transient boolean is_public; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 1f36a3c6265f..f4a988ab9be2 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -29,10 +29,7 @@ import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; @@ -289,7 +286,9 @@ public class StudyProjectGenerator { } public static void flushCourseJson(@NotNull final Course course, @NotNull final File courseDirectory) { - final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); + final Gson gson = new GsonBuilder().setPrettyPrinting(). + registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()). + excludeFieldsWithoutExposeAnnotation().create(); final String json = gson.toJson(course); final File courseJson = new File(courseDirectory, EduNames.COURSE_META_FILE); final FileOutputStream fileOutputStream; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 171de5e6cd18..2698f7487177 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -20,10 +20,7 @@ import com.jetbrains.edu.learning.StudySerializationUtils; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.*; import org.apache.commons.codec.binary.Base64; import org.apache.http.*; import org.apache.http.client.entity.UrlEncodedFormEntity; @@ -237,10 +234,12 @@ public class EduStepicConnector { if (statusLine.getStatusCode() != HttpStatus.SC_OK) { throw new IOException("Stepic returned non 200 status code " + responseString); } - Gson gson = new GsonBuilder().registerTypeAdapter(TaskFile.class, new StudySerializationUtils.Json.StepicTaskFileAdapter()).setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + Gson gson = new GsonBuilder().registerTypeAdapter(TaskFile.class, new StudySerializationUtils.Json.StepicTaskFileAdapter()). + registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()). + setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); return gson.fromJson(responseString, container); } - + @NotNull public static CloseableHttpClient getHttpClient(@NotNull final Project project) { if (ourClient == null) { @@ -727,7 +726,8 @@ public class EduStepicConnector { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/step-sources"); setHeaders(request, "application/json"); //TODO: register type adapter for task files here? - final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); + final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). + registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); ApplicationManager.getApplication().invokeLater(() -> { final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));