e743ea13f3bcadc582be69ba5c43b54b1f9a19fc
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / stepic / EduStepicConnector.java
1 package com.jetbrains.edu.learning.stepic;
2
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;
34
35 import java.io.IOException;
36 import java.net.URI;
37 import java.net.URISyntaxException;
38 import java.util.*;
39
40 public class EduStepicConnector {
41   private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName());
42
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!";
47
48   private EduStepicConnector() {
49   }
50
51   public static boolean enrollToCourse(final int courseId) {
52     HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ENROLLMENTS);
53     try {
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;
60     }
61     catch (IOException e) {
62       LOG.warn(e.getMessage());
63     }
64     return false;
65   }
66
67   @NotNull
68   public static List<CourseInfo> getCourses() {
69     try {
70       List<CourseInfo> result = new ArrayList<>();
71       int pageNumber = 1;
72       while (addCoursesFromStepic(result, pageNumber)) {
73         pageNumber += 1;
74       }
75       return result;
76     }
77     catch (IOException e) {
78       LOG.error("Cannot load course list " + e.getMessage());
79     }
80     return Collections.singletonList(CourseInfo.INVALID_COURSE);
81   }
82
83   public static CourseInfo getCourseInfo(Project project, String courseId) {
84     final String url = EduStepicNames.COURSES + "/" + courseId;
85     try {
86       final StepicWrappers.CoursesContainer coursesContainer =
87         EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project);
88       return coursesContainer.courses.get(0);
89     }
90     catch (IOException e) {
91       LOG.error(e.getMessage());
92     }
93     return null;
94   }
95
96   public static Date getCourseUpdateDate(final int courseId) {
97     final String url = EduStepicNames.COURSES + "/" + courseId;
98     try {
99       final List<CourseInfo> courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses;
100       if (!courses.isEmpty()) {
101         return courses.get(0).getUpdateDate();
102       }
103     }
104     catch (IOException e) {
105       LOG.warn("Could not retrieve course with id=" + courseId);
106     }
107
108     return null;
109   }
110
111   public static Date getLessonUpdateDate(final int lessonId) {
112     final String url = EduStepicNames.LESSONS + "/" + lessonId;
113     try {
114       List<Lesson> lessons = EduStepicClient.getFromStepic(url, StepicWrappers.LessonContainer.class).lessons;
115       if (!lessons.isEmpty()) {
116         return lessons.get(0).getUpdateDate();
117       }
118     }
119     catch (IOException e) {
120       LOG.warn("Could not retrieve course with id=" + lessonId);
121     }
122
123     return null;
124   }
125
126   public static Date getTaskUpdateDate(final int taskId) {
127     final String url = EduStepicNames.STEPS + "/" + String.valueOf(taskId);
128     try {
129       List<StepicWrappers.StepSource> steps = EduStepicClient.getFromStepic(url, StepicWrappers.StepContainer.class).steps;
130       if (!steps.isEmpty()) {
131         return steps.get(0).update_date;
132       }
133     }
134     catch (IOException e) {
135       LOG.warn("Could not retrieve course with id=" + taskId);
136     }
137
138     return null;
139   }
140
141   private static boolean addCoursesFromStepic(List<CourseInfo> result, int pageNumber) throws IOException {
142     final URI url;
143     try {
144       url = new URIBuilder(EduStepicNames.COURSES).addParameter("is_idea_compatible", "true").
145           addParameter("page", String.valueOf(pageNumber)).build();
146     }
147     catch (URISyntaxException e) {
148       LOG.error(e.getMessage());
149       return false;
150     }
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);
162         }
163         
164         if (info.isAdaptive()) {
165           info.setDescription("This is a Stepik Adaptive course.\n\n" + info.getDescription() + ADAPTIVE_NOTE);
166         }
167         
168         result.add(info);
169       }
170     }
171     return coursesContainer.meta.containsKey("has_next") && coursesContainer.meta.get("has_next") == Boolean.TRUE;
172   }
173
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()));
181     
182     if (!course.isAdaptive()) {
183       String courseType = info.getType();
184       course.setName(info.getName());
185       course.setLanguage(courseType.substring(PYCHARM_PREFIX.length() + 1));
186       try {
187         for (Integer section : info.sections) {
188           course.addLessons(getLessons(section));
189         }
190         return course;
191       }
192       catch (IOException e) {
193         LOG.error("IOException " + e.getMessage());
194       }
195     }
196     else {
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);
205         return course;
206       }
207       else {
208         return null;
209       }
210     }
211     return null;
212   }
213
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);
229       }
230       if (!lesson.taskList.isEmpty())
231         lessons.add(lesson);
232     }
233
234     return lessons;
235   }
236
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);
248     }
249
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);
254       }
255     }
256     lesson.taskList.add(task);
257   }
258
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);
261   }
262
263   public static void postAttempt(@NotNull final Task task, boolean passed, @NotNull final Project project) {
264     if (task.getStepicId() <= 0) {
265       return;
266     }
267
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));
271
272     try {
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);
281       }
282       final StepicWrappers.AttemptWrapper.Attempt attempt = new Gson().fromJson(attemptResponseString, StepicWrappers.AttemptContainer.class).attempts.get(0);
283
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));
288       }
289       postSubmission(passed, attempt, project, files);
290     }
291     catch (IOException e) {
292       LOG.error(e.getMessage());
293     }
294   }
295
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);
299
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);
310     }
311   }
312
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) {
315       @Override
316       public void run(@NotNull final ProgressIndicator indicator) {
317         postCourse(project, course, indicator);
318       }
319     });
320   }
321
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");
325
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());
334         }
335       }
336       course.setAuthors(Collections.singletonList(currentUser));
337     }
338
339     String requestBody = new Gson().toJson(new StepicWrappers.CourseWrapper(course));
340     request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
341
342     try {
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);
351         return;
352       }
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()));
356       int position = 1;
357       for (Lesson lesson : course.getLessons()) {
358         indicator.checkCanceled();
359         final int lessonId = postLesson(project, lesson, indicator);
360         postUnit(project, lessonId, position, sectionId);
361         position += 1;
362       }
363       ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator));
364     }
365     catch (IOException e) {
366       LOG.error(e.getMessage());
367     }
368   }
369
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() {
373       @Override
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(".");
378       }
379     });
380
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);
388       task.setIndex(1);
389       task.setText(EduNames.PYCHARM_ADDITIONAL);
390       for (VirtualFile file : files) {
391         try {
392           if (file != null) {
393             if (EduUtils.isImage(file.getName())) {
394               task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream())));
395             }
396             else {
397               task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream()));
398             }
399           }
400         }
401         catch (IOException e) {
402           LOG.error("Can't find file " + file.getPath());
403         }
404       }
405       lesson.addTask(task);
406       lesson.setIndex(1);
407       final int lessonId = postLesson(project, lesson, indicator);
408       postUnit(project, lessonId, 1, sectionId);
409     }
410   }
411
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;
419
420     String requestBody = new Gson().toJson(unitWrapper);
421     request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
422
423     try {
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);
432       }
433     }
434     catch (IOException e) {
435       LOG.error(e.getMessage());
436     }
437   }
438
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));
449
450     try {
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);
459         return -1;
460       }
461       final StepicWrappers.Section
462         postedSection = new Gson().fromJson(responseString, StepicWrappers.SectionContainer.class).sections.get(0);
463       return postedSection.id;
464     }
465     catch (IOException e) {
466       LOG.error(e.getMessage());
467     }
468     return -1;
469   }
470
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();
474
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));
482
483       try {
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);
492         }
493       }
494       catch (IOException e) {
495         LOG.error(e.getMessage());
496       }
497     });
498     return -1;
499   }
500
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()));
503
504     String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
505     request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
506
507     try {
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);
516         return -1;
517       }
518       final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
519       for (Integer step : postedLesson.steps) {
520         deleteTask(step, project);
521       }
522
523       for (Task task : lesson.getTaskList()) {
524         indicator.checkCanceled();
525         postTask(project, task, lesson.getId());
526       }
527       return lesson.getId();
528     }
529     catch (IOException e) {
530       LOG.error(e.getMessage());
531     }
532     return -1;
533   }
534
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");
537
538     String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
539     request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
540
541     try {
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);
550         return 0;
551       }
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());
557       }
558       return postedLesson.getId();
559     }
560     catch (IOException e) {
561       LOG.error(e.getMessage());
562     }
563     return -1;
564   }
565
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(() -> {
569       try {
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);
578         }
579       }
580       catch (IOException e) {
581         LOG.error(e.getMessage());
582       }
583     });
584   }
585
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));
594
595       try {
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);
604           return;
605         }
606
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());
610       }
611       catch (IOException e) {
612         LOG.error(e.getMessage());
613       }
614     });
615   }
616 }