1 package com.jetbrains.edu.learning.stepic;
3 import com.google.gson.Gson;
4 import com.google.gson.GsonBuilder;
5 import com.google.gson.JsonObject;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.diagnostic.Logger;
8 import com.intellij.openapi.progress.ProgressIndicator;
9 import com.intellij.openapi.progress.ProgressManager;
10 import com.intellij.openapi.project.Project;
11 import com.intellij.openapi.util.io.FileUtil;
12 import com.intellij.openapi.util.text.StringUtil;
13 import com.intellij.openapi.vfs.VfsUtil;
14 import com.intellij.openapi.vfs.VirtualFile;
15 import com.intellij.openapi.vfs.VirtualFileFilter;
16 import com.jetbrains.edu.learning.StudySerializationUtils;
17 import com.jetbrains.edu.learning.core.EduNames;
18 import com.jetbrains.edu.learning.core.EduUtils;
19 import com.jetbrains.edu.learning.courseFormat.*;
20 import org.apache.commons.codec.binary.Base64;
21 import org.apache.http.HttpEntity;
22 import org.apache.http.HttpStatus;
23 import org.apache.http.StatusLine;
24 import org.apache.http.client.methods.CloseableHttpResponse;
25 import org.apache.http.client.methods.HttpDelete;
26 import org.apache.http.client.methods.HttpPost;
27 import org.apache.http.client.methods.HttpPut;
28 import org.apache.http.client.utils.URIBuilder;
29 import org.apache.http.entity.ContentType;
30 import org.apache.http.entity.StringEntity;
31 import org.apache.http.impl.client.CloseableHttpClient;
32 import org.apache.http.util.EntityUtils;
33 import org.jetbrains.annotations.NotNull;
35 import java.io.IOException;
37 import java.net.URISyntaxException;
40 public class EduStepicConnector {
41 private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName());
43 //this prefix indicates that course can be opened by educational plugin
44 public static final String PYCHARM_PREFIX = "pycharm";
45 private static final String ADAPTIVE_NOTE =
46 "\n\nInitially, the adaptive system may behave somewhat randomly, but the more problems you solve, the smarter it become!";
48 private EduStepicConnector() {
51 public static boolean enrollToCourse(final int courseId) {
52 HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ENROLLMENTS);
54 final StepicWrappers.EnrollmentWrapper enrollment = new StepicWrappers.EnrollmentWrapper(String.valueOf(courseId));
55 post.setEntity(new StringEntity(new GsonBuilder().create().toJson(enrollment)));
56 final CloseableHttpClient client = EduStepicClient.getHttpClient();
57 CloseableHttpResponse response = client.execute(post);
58 StatusLine line = response.getStatusLine();
59 return line.getStatusCode() == HttpStatus.SC_CREATED;
61 catch (IOException e) {
62 LOG.warn(e.getMessage());
68 public static List<CourseInfo> getCourses() {
70 List<CourseInfo> result = new ArrayList<>();
72 while (addCoursesFromStepic(result, pageNumber)) {
77 catch (IOException e) {
78 LOG.error("Cannot load course list " + e.getMessage());
80 return Collections.singletonList(CourseInfo.INVALID_COURSE);
83 public static CourseInfo getCourseInfo(Project project, String courseId) {
84 final String url = EduStepicNames.COURSES + "/" + courseId;
86 final StepicWrappers.CoursesContainer coursesContainer =
87 EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project);
88 return coursesContainer.courses.get(0);
90 catch (IOException e) {
91 LOG.error(e.getMessage());
96 public static Date getCourseUpdateDate(final int courseId) {
97 final String url = EduStepicNames.COURSES + "/" + courseId;
99 final List<CourseInfo> courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses;
100 if (!courses.isEmpty()) {
101 return courses.get(0).getUpdateDate();
104 catch (IOException e) {
105 LOG.warn("Could not retrieve course with id=" + courseId);
111 public static Date getLessonUpdateDate(final int lessonId) {
112 final String url = EduStepicNames.LESSONS + "/" + lessonId;
114 List<Lesson> lessons = EduStepicClient.getFromStepic(url, StepicWrappers.LessonContainer.class).lessons;
115 if (!lessons.isEmpty()) {
116 return lessons.get(0).getUpdateDate();
119 catch (IOException e) {
120 LOG.warn("Could not retrieve course with id=" + lessonId);
126 public static Date getTaskUpdateDate(final int taskId) {
127 final String url = EduStepicNames.STEPS + "/" + String.valueOf(taskId);
129 List<StepicWrappers.StepSource> steps = EduStepicClient.getFromStepic(url, StepicWrappers.StepContainer.class).steps;
130 if (!steps.isEmpty()) {
131 return steps.get(0).update_date;
134 catch (IOException e) {
135 LOG.warn("Could not retrieve course with id=" + taskId);
141 private static boolean addCoursesFromStepic(List<CourseInfo> result, int pageNumber) throws IOException {
144 url = new URIBuilder(EduStepicNames.COURSES).addParameter("is_idea_compatible", "true").
145 addParameter("page", String.valueOf(pageNumber)).build();
147 catch (URISyntaxException e) {
148 LOG.error(e.getMessage());
151 final StepicWrappers.CoursesContainer coursesContainer = EduStepicClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class);
152 final List<CourseInfo> courseInfos = coursesContainer.courses;
153 for (CourseInfo info : courseInfos) {
154 final String courseType = info.getType();
155 if (!info.isAdaptive() && StringUtil.isEmptyOrSpaces(courseType)) continue;
156 final List<String> typeLanguage = StringUtil.split(courseType, " ");
157 if (info.isAdaptive() || (typeLanguage.size() == 2 && PYCHARM_PREFIX.equals(typeLanguage.get(0)))) {
158 for (Integer instructor : info.instructors) {
159 final StepicUser author = EduStepicClient.getFromStepic(EduStepicNames.USERS + "/" + String.valueOf(instructor),
160 StepicWrappers.AuthorWrapper.class).users.get(0);
161 info.addAuthor(author);
164 if (info.isAdaptive()) {
165 info.setDescription("This is a Stepik Adaptive course.\n\n" + info.getDescription() + ADAPTIVE_NOTE);
171 return coursesContainer.meta.containsKey("has_next") && coursesContainer.meta.get("has_next") == Boolean.TRUE;
174 public static Course getCourse(@NotNull final Project project, @NotNull final CourseInfo info) {
175 final Course course = new Course();
176 course.setAuthors(info.getAuthors());
177 course.setDescription(info.getDescription());
178 course.setAdaptive(info.isAdaptive());
179 course.setId(info.getId());
180 course.setUpdateDate(getCourseUpdateDate(info.getId()));
182 if (!course.isAdaptive()) {
183 String courseType = info.getType();
184 course.setName(info.getName());
185 course.setLanguage(courseType.substring(PYCHARM_PREFIX.length() + 1));
187 for (Integer section : info.sections) {
188 course.addLessons(getLessons(section));
192 catch (IOException e) {
193 LOG.error("IOException " + e.getMessage());
197 final Lesson lesson = new Lesson();
198 course.setName(info.getName());
199 //TODO: more specific name?
200 lesson.setName("Adaptive");
201 course.addLesson(lesson);
202 final Task recommendation = EduAdaptiveStepicConnector.getNextRecommendation(project, course);
203 if (recommendation != null) {
204 lesson.addTask(recommendation);
214 public static List<Lesson> getLessons(int sectionId) throws IOException {
215 final StepicWrappers.SectionContainer
216 sectionContainer = EduStepicClient.getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), StepicWrappers.SectionContainer.class);
217 List<Integer> unitIds = sectionContainer.sections.get(0).units;
218 final List<Lesson> lessons = new ArrayList<>();
219 for (Integer unitId : unitIds) {
220 StepicWrappers.UnitContainer
221 unit = EduStepicClient.getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class);
222 int lessonID = unit.units.get(0).lesson;
223 StepicWrappers.LessonContainer
224 lessonContainer = EduStepicClient.getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), StepicWrappers.LessonContainer.class);
225 Lesson lesson = lessonContainer.lessons.get(0);
226 lesson.taskList = new ArrayList<>();
227 for (Integer s : lesson.steps) {
228 createTask(lesson, s);
230 if (!lesson.taskList.isEmpty())
237 private static void createTask(Lesson lesson, Integer stepicId) throws IOException {
238 final StepicWrappers.StepSource step = getStep(stepicId);
239 final StepicWrappers.Step block = step.block;
240 if (!block.name.equals(PYCHARM_PREFIX)) return;
241 final Task task = new Task();
242 task.setStepicId(stepicId);
243 task.setUpdateDate(step.update_date);
244 task.setName(block.options != null ? block.options.title : PYCHARM_PREFIX);
245 task.setText(block.text);
246 for (StepicWrappers.TestFileWrapper wrapper : block.options.test) {
247 task.addTestsTexts(wrapper.name, wrapper.text);
250 task.taskFiles = new HashMap<>(); // TODO: it looks like we don't need taskFiles as map anymore
251 if (block.options.files != null) {
252 for (TaskFile taskFile : block.options.files) {
253 task.taskFiles.put(taskFile.name, taskFile);
256 lesson.taskList.add(task);
259 public static StepicWrappers.StepSource getStep(Integer step) throws IOException {
260 return EduStepicClient.getFromStepic(EduStepicNames.STEPS + "/" + String.valueOf(step), StepicWrappers.StepContainer.class).steps.get(0);
263 public static void postAttempt(@NotNull final Task task, boolean passed, @NotNull final Project project) {
264 if (task.getStepicId() <= 0) {
268 final HttpPost attemptRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS);
269 String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(task.getStepicId()));
270 attemptRequest.setEntity(new StringEntity(attemptRequestBody, ContentType.APPLICATION_JSON));
273 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
274 final CloseableHttpResponse attemptResponse = client.execute(attemptRequest);
275 final HttpEntity responseEntity = attemptResponse.getEntity();
276 final String attemptResponseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
277 final StatusLine statusLine = attemptResponse.getStatusLine();
278 EntityUtils.consume(responseEntity);
279 if (statusLine.getStatusCode() != HttpStatus.SC_CREATED) {
280 LOG.warn("Failed to make attempt " + attemptResponseString);
282 final StepicWrappers.AttemptWrapper.Attempt attempt = new Gson().fromJson(attemptResponseString, StepicWrappers.AttemptContainer.class).attempts.get(0);
284 final Map<String, TaskFile> taskFiles = task.getTaskFiles();
285 final ArrayList<StepicWrappers.SolutionFile> files = new ArrayList<>();
286 for (TaskFile fileEntry : taskFiles.values()) {
287 files.add(new StepicWrappers.SolutionFile(fileEntry.name, fileEntry.text));
289 postSubmission(passed, attempt, project, files);
291 catch (IOException e) {
292 LOG.error(e.getMessage());
296 private static void postSubmission(boolean passed, StepicWrappers.AttemptWrapper.Attempt attempt,
297 Project project, ArrayList<StepicWrappers.SolutionFile> files) throws IOException {
298 final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.SUBMISSIONS);
300 String requestBody = new Gson().toJson(new StepicWrappers.SubmissionWrapper(attempt.id, passed ? "1" : "0", files));
301 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
302 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
303 final CloseableHttpResponse response = client.execute(request);
304 final HttpEntity responseEntity = response.getEntity();
305 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
306 final StatusLine line = response.getStatusLine();
307 EntityUtils.consume(responseEntity);
308 if (line.getStatusCode() != HttpStatus.SC_CREATED) {
309 LOG.error("Failed to make submission " + responseString);
313 public static void postCourseWithProgress(final Project project, @NotNull final Course course) {
314 ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) {
316 public void run(@NotNull final ProgressIndicator indicator) {
317 postCourse(project, course, indicator);
322 private static void postCourse(final Project project, @NotNull Course course, @NotNull final ProgressIndicator indicator) {
323 indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL);
324 final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses");
326 final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser();
327 if (currentUser != null) {
328 final List<StepicUser> courseAuthors = course.getAuthors();
329 for (int i = 0; i < courseAuthors.size(); i++) {
330 if (courseAuthors.size() > i) {
331 final StepicUser courseAuthor = courseAuthors.get(i);
332 currentUser.setFirstName(courseAuthor.getFirstName());
333 currentUser.setLastName(courseAuthor.getLastName());
336 course.setAuthors(Collections.singletonList(currentUser));
339 String requestBody = new Gson().toJson(new StepicWrappers.CourseWrapper(course));
340 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
343 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
344 final CloseableHttpResponse response = client.execute(request);
345 final HttpEntity responseEntity = response.getEntity();
346 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
347 final StatusLine line = response.getStatusLine();
348 EntityUtils.consume(responseEntity);
349 if (line.getStatusCode() != HttpStatus.SC_CREATED) {
350 LOG.error("Failed to push " + responseString);
353 final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0);
354 course.setId(postedCourse.id);
355 final int sectionId = postModule(project, postedCourse.id, 1, String.valueOf(postedCourse.getName()));
357 for (Lesson lesson : course.getLessons()) {
358 indicator.checkCanceled();
359 final int lessonId = postLesson(project, lesson, indicator);
360 postUnit(project, lessonId, position, sectionId);
363 ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator));
365 catch (IOException e) {
366 LOG.error(e.getMessage());
370 private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) {
371 final VirtualFile baseDir = project.getBaseDir();
372 final List<VirtualFile> files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() {
374 public boolean accept(VirtualFile file) {
375 final String name = file.getName();
376 return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) &&
377 !"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith(".");
381 if (!files.isEmpty()) {
382 final int sectionId = postModule(project, id, 2, EduNames.PYCHARM_ADDITIONAL);
383 final Lesson lesson = new Lesson();
384 lesson.setName(EduNames.PYCHARM_ADDITIONAL);
385 final Task task = new Task();
386 task.setLesson(lesson);
387 task.setName(EduNames.PYCHARM_ADDITIONAL);
389 task.setText(EduNames.PYCHARM_ADDITIONAL);
390 for (VirtualFile file : files) {
393 if (EduUtils.isImage(file.getName())) {
394 task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream())));
397 task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream()));
401 catch (IOException e) {
402 LOG.error("Can't find file " + file.getPath());
405 lesson.addTask(task);
407 final int lessonId = postLesson(project, lesson, indicator);
408 postUnit(project, lessonId, 1, sectionId);
412 public static void postUnit(@NotNull final Project project, int lessonId, int position, int sectionId) {
413 final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS);
414 final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper();
415 unitWrapper.unit = new StepicWrappers.Unit();
416 unitWrapper.unit.lesson = lessonId;
417 unitWrapper.unit.position = position;
418 unitWrapper.unit.section = sectionId;
420 String requestBody = new Gson().toJson(unitWrapper);
421 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
424 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
425 final CloseableHttpResponse response = client.execute(request);
426 final HttpEntity responseEntity = response.getEntity();
427 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
428 final StatusLine line = response.getStatusLine();
429 EntityUtils.consume(responseEntity);
430 if (line.getStatusCode() != HttpStatus.SC_CREATED) {
431 LOG.error("Failed to push " + responseString);
434 catch (IOException e) {
435 LOG.error(e.getMessage());
439 private static int postModule(@NotNull final Project project, int courseId, int position, @NotNull final String title) {
440 final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections");
441 final StepicWrappers.Section section = new StepicWrappers.Section();
442 section.course = courseId;
443 section.title = title;
444 section.position = position;
445 final StepicWrappers.SectionWrapper sectionContainer = new StepicWrappers.SectionWrapper();
446 sectionContainer.section = section;
447 String requestBody = new Gson().toJson(sectionContainer);
448 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
451 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
452 final CloseableHttpResponse response = client.execute(request);
453 final HttpEntity responseEntity = response.getEntity();
454 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
455 final StatusLine line = response.getStatusLine();
456 EntityUtils.consume(responseEntity);
457 if (line.getStatusCode() != HttpStatus.SC_CREATED) {
458 LOG.error("Failed to push " + responseString);
461 final StepicWrappers.Section
462 postedSection = new Gson().fromJson(responseString, StepicWrappers.SectionContainer.class).sections.get(0);
463 return postedSection.id;
465 catch (IOException e) {
466 LOG.error(e.getMessage());
471 public static int updateTask(@NotNull final Project project, @NotNull final Task task) {
472 final Lesson lesson = task.getLesson();
473 final int lessonId = lesson.getId();
475 final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepicId()));
476 final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().
477 registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create();
478 ApplicationManager.getApplication().invokeLater(() -> {
479 task.addTestsTexts("tests.py", task.getTestsText(project));
480 final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
481 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
484 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
485 final CloseableHttpResponse response = client.execute(request);
486 final HttpEntity responseEntity = response.getEntity();
487 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
488 EntityUtils.consume(responseEntity);
489 final StatusLine line = response.getStatusLine();
490 if (line.getStatusCode() != HttpStatus.SC_OK) {
491 LOG.error("Failed to push " + responseString);
494 catch (IOException e) {
495 LOG.error(e.getMessage());
501 public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
502 final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId()));
504 String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
505 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
508 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
509 final CloseableHttpResponse response = client.execute(request);
510 final HttpEntity responseEntity = response.getEntity();
511 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
512 final StatusLine line = response.getStatusLine();
513 EntityUtils.consume(responseEntity);
514 if (line.getStatusCode() != HttpStatus.SC_OK) {
515 LOG.error("Failed to push " + responseString);
518 final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
519 for (Integer step : postedLesson.steps) {
520 deleteTask(step, project);
523 for (Task task : lesson.getTaskList()) {
524 indicator.checkCanceled();
525 postTask(project, task, lesson.getId());
527 return lesson.getId();
529 catch (IOException e) {
530 LOG.error(e.getMessage());
535 public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
536 final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons");
538 String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
539 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
542 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
543 final CloseableHttpResponse response = client.execute(request);
544 final HttpEntity responseEntity = response.getEntity();
545 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
546 final StatusLine line = response.getStatusLine();
547 EntityUtils.consume(responseEntity);
548 if (line.getStatusCode() != HttpStatus.SC_CREATED) {
549 LOG.error("Failed to push " + responseString);
552 final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
553 lesson.setId(postedLesson.getId());
554 for (Task task : lesson.getTaskList()) {
555 indicator.checkCanceled();
556 postTask(project, task, postedLesson.getId());
558 return postedLesson.getId();
560 catch (IOException e) {
561 LOG.error(e.getMessage());
566 public static void deleteTask(@NotNull final Integer task, Project project) {
567 final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task);
568 ApplicationManager.getApplication().invokeLater(() -> {
570 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
571 final CloseableHttpResponse response = client.execute(request);
572 final HttpEntity responseEntity = response.getEntity();
573 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
574 EntityUtils.consume(responseEntity);
575 final StatusLine line = response.getStatusLine();
576 if (line.getStatusCode() != HttpStatus.SC_NO_CONTENT) {
577 LOG.error("Failed to delete task " + responseString);
580 catch (IOException e) {
581 LOG.error(e.getMessage());
586 public static void postTask(final Project project, @NotNull final Task task, final int lessonId) {
587 final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/step-sources");
588 //TODO: register type adapter for task files here?
589 final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().
590 registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create();
591 ApplicationManager.getApplication().invokeLater(() -> {
592 final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
593 request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
596 final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project);
597 final CloseableHttpResponse response = client.execute(request);
598 final StatusLine line = response.getStatusLine();
599 final HttpEntity responseEntity = response.getEntity();
600 final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : "";
601 EntityUtils.consume(responseEntity);
602 if (line.getStatusCode() != HttpStatus.SC_CREATED) {
603 LOG.error("Failed to push " + responseString);
607 final JsonObject postedTask = new Gson().fromJson(responseString, JsonObject.class);
608 final JsonObject stepSource = postedTask.getAsJsonArray("step-sources").get(0).getAsJsonObject();
609 task.setStepicId(stepSource.getAsJsonPrimitive("id").getAsInt());
611 catch (IOException e) {
612 LOG.error(e.getMessage());