get rid of courseInfo

extracted remoteCourse for stepik specific fields
This commit is contained in:
Ekaterina Tuzova
2017-04-06 14:54:32 +03:00
committed by Ekaterina Tuzova
parent b9faa00c19
commit 8eadfc678b
27 changed files with 340 additions and 422 deletions
@@ -220,7 +220,7 @@ public class CCCreateCourseArchive extends DumbAwareAction {
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
private static void generateJson(VirtualFile parentDir, Course course) {
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().
registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskSerializer()).create();
registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskAdapter()).create();
final String json = gson.toJson(course);
final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE);
OutputStreamWriter outputStreamWriter = null;
@@ -70,7 +70,7 @@ public class CCFromCourseArchive extends DumbAwareAction {
reader = new InputStreamReader(new FileInputStream(courseMetaFile));
Gson gson = new GsonBuilder()
.registerTypeAdapter(Course.class, new StudySerializationUtils.Json.CourseTypeAdapter(courseMetaFile))
.registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskDeserializer())
.registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskAdapter())
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
Course course = gson.fromJson(reader, Course.class);
@@ -20,8 +20,8 @@ import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
@@ -58,7 +58,7 @@ public class CCGetCourseFromStepic extends DumbAwareAction {
private static void createCourse(Project project, String courseId) {
final VirtualFile baseDir = project.getBaseDir();
final CourseInfo info = CCStepicConnector.getCourseInfo(courseId);
final RemoteCourse info = CCStepicConnector.getCourseInfo(courseId);
if (info == null) return;
final Course course = EduStepicConnector.getCourse(project, info);
@@ -14,8 +14,8 @@ import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
import org.jetbrains.annotations.NotNull;
@@ -33,11 +33,8 @@ public class CCPushCourse extends DumbAwareAction {
presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project));
if (project != null) {
final Course course = StudyTaskManager.getInstance(project).getCourse();
if (course != null) {
final int id = course.getId();
if (id > 0) {
presentation.setText("Update Course on Stepik");
}
if (course instanceof RemoteCourse) {
presentation.setText("Update Course on Stepik");
}
}
}
@@ -53,7 +50,7 @@ public class CCPushCourse extends DumbAwareAction {
if (course == null) {
return;
}
if (course.getId() > 0) {
if (course instanceof RemoteCourse) {
ProgressManager.getInstance().run(new Task.Modal(project, "Updating Course", true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
@@ -62,10 +59,9 @@ public class CCPushCourse extends DumbAwareAction {
CCStepicConnector.updateLesson(project, lesson);
}
else {
final CourseInfo info = CourseInfo.fromCourse(course);
final int lessonId = CCStepicConnector.postLesson(project, lesson);
if (lessonId != -1) {
final List<Integer> sections = info.getSections();
final List<Integer> sections = ((RemoteCourse)course).getSections();
final Integer sectionId = sections.get(sections.size() - 1);
CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId);
}
@@ -15,8 +15,8 @@ import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.stepic.EduStepicNames;
import org.jetbrains.annotations.NotNull;
@@ -36,7 +36,7 @@ public class CCPushLesson extends DumbAwareAction {
return;
}
final Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null) {
if (course == null || !(course instanceof RemoteCourse)) {
return;
}
if (!course.getCourseMode().equals(CCUtils.COURSE_MODE)) return;
@@ -45,7 +45,7 @@ public class CCPushLesson extends DumbAwareAction {
return;
}
final Lesson lesson = course.getLesson(lessonDir.getName());
if (lesson != null && course.getId() > 0) {
if (lesson != null && ((RemoteCourse)course).getId() > 0) {
e.getPresentation().setEnabledAndVisible(true);
if (lesson.getId() <= 0) {
e.getPresentation().setText("Upload Lesson to Stepik");
@@ -61,7 +61,7 @@ public class CCPushLesson extends DumbAwareAction {
return;
}
final Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null) {
if (course == null || !(course instanceof RemoteCourse)) {
return;
}
PsiDirectory lessonDir = DirectoryChooserUtil.getOrChooseDirectory(view);
@@ -80,9 +80,8 @@ public class CCPushLesson extends DumbAwareAction {
CCStepicConnector.updateLesson(project, lesson);
}
else {
final CourseInfo info = CourseInfo.fromCourse(course);
final int lessonId = CCStepicConnector.postLesson(project, lesson);
final List<Integer> sections = info.getSections();
final List<Integer> sections = ((RemoteCourse)course).getSections();
final Integer sectionId = sections.get(sections.size()-1);
CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId);
}
@@ -12,11 +12,12 @@ import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.stepic.EduStepicNames;
import org.jetbrains.annotations.NotNull;
@@ -34,7 +35,7 @@ public class CCPushTask extends DumbAwareAction {
return;
}
final Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null) {
if (course == null || !(course instanceof RemoteCourse)) {
return;
}
if (!course.getCourseMode().equals(CCUtils.COURSE_MODE)) return;
@@ -45,7 +46,7 @@ public class CCPushTask extends DumbAwareAction {
final PsiDirectory lessonDir = taskDir.getParentDirectory();
if (lessonDir == null) return;
final Lesson lesson = course.getLesson(lessonDir.getName());
if (lesson != null && lesson.getId() > 0 && course.getId() > 0) {
if (lesson != null && lesson.getId() > 0 && ((RemoteCourse)course).getId() > 0) {
e.getPresentation().setEnabledAndVisible(true);
final com.jetbrains.edu.learning.courseFormat.tasks.Task task = lesson.getTask(taskDir.getName());
if (task.getStepId() <= 0) {
@@ -10,11 +10,12 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.StudyTaskManager;
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.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient;
import com.jetbrains.edu.learning.stepic.EduStepicNames;
@@ -43,7 +44,7 @@ public class CCStepicConnector {
private CCStepicConnector() {
}
public static CourseInfo getCourseInfo(String courseId) {
public static RemoteCourse getCourseInfo(String courseId) {
final String url = EduStepicNames.COURSES + "/" + courseId;
try {
final StepicWrappers.CoursesContainer coursesContainer =
@@ -103,8 +104,10 @@ public class CCStepicConnector {
LOG.error("Failed to push " + responseString);
return;
}
final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0);
course.setId(postedCourse.getId());
final RemoteCourse postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0);
postedCourse.setLessons(course.getLessons(true));
postedCourse.setAuthors(course.getAuthors());
postedCourse.setCourseMode(CCUtils.COURSE_MODE);
final int sectionId = postModule(postedCourse.getId(), 1, String.valueOf(postedCourse.getName()));
int position = 1;
for (Lesson lesson : course.getLessons()) {
@@ -116,6 +119,7 @@ public class CCStepicConnector {
position += 1;
}
ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(course, project, postedCourse.getId()));
StudyTaskManager.getInstance(project).setCourse(postedCourse);
}
catch (IOException e) {
LOG.error(e.getMessage());
@@ -241,7 +245,7 @@ public class CCStepicConnector {
LOG.error("Failed to push " + responseString);
return -1;
}
final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
final Lesson postedLesson = new Gson().fromJson(responseString, RemoteCourse.class).getLessons().get(0);
for (Integer step : postedLesson.steps) {
deleteTask(step);
}
@@ -279,7 +283,7 @@ public class CCStepicConnector {
LOG.error("Failed to push " + responseString);
return 0;
}
final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
final Lesson postedLesson = new Gson().fromJson(responseString, RemoteCourse.class).getLessons(true).get(0);
lesson.setId(postedLesson.getId());
for (Task task : lesson.getTaskList()) {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
@@ -146,10 +146,8 @@ public class StudyProjectComponent implements ProjectComponent {
private void updateCourse() {
final Course currentCourse = StudyTaskManager.getInstance(myProject).getCourse();
final CourseInfo info = CourseInfo.fromCourse(currentCourse);
if (info == null) return;
final Course course = EduStepicConnector.getCourse(myProject, info);
if (currentCourse == null || !(currentCourse instanceof RemoteCourse)) return;
final Course course = EduStepicConnector.getCourse(myProject, (RemoteCourse)currentCourse);
if (course == null) return;
course.initCourse(false);
@@ -16,6 +16,7 @@ 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.RemoteCourse;
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.tasks.*;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
@@ -62,6 +63,7 @@ public class StudySerializationUtils {
public static class Xml {
public final static String COURSE_ELEMENT = "courseElement";
public final static String MAIN_ELEMENT = "StudyTaskManager";
public final static String REMOTE_COURSE = "RemoteCourse";
public static final String MAP = "map";
public static final String KEY = "key";
public static final String VALUE = "value";
@@ -498,7 +500,7 @@ public class StudySerializationUtils {
}
}
}
return new GsonBuilder().registerTypeAdapter(Task.class, new TaskDeserializer()).create().fromJson(json, Course.class);
return new GsonBuilder().registerTypeAdapter(Task.class, new TaskAdapter()).create().fromJson(json, Course.class);
}
private static void convertToAbsoluteOffset(Document document, JsonElement placeholder) {
@@ -676,7 +678,23 @@ public class StudySerializationUtils {
subtaskInfo.addProperty(POSSIBLE_ANSWER, placeholderObject.getAsJsonPrimitive(POSSIBLE_ANSWER).getAsString());
}
public static class TaskSerializer implements JsonSerializer<Task> {
public static class StepikCourseSerializer implements JsonSerializer<Course>, JsonDeserializer<Course> {
@Override
public Course deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return null;
}
@Override
public JsonElement serialize(Course src, Type typeOfSrc, JsonSerializationContext context) {
if (src instanceof RemoteCourse) {
context.serialize(src, RemoteCourse.class);
}
return context.serialize(src);
}
}
public static class TaskAdapter implements JsonSerializer<Task>, JsonDeserializer<Task> {
@Override
public JsonElement serialize(Task src, Type typeOfSrc, JsonSerializationContext context) {
@@ -686,9 +704,6 @@ public class StudySerializationUtils {
task.add(TASK_TYPE, new JsonPrimitive(src.getTaskType()));
return task;
}
}
public static class TaskDeserializer implements JsonDeserializer<Task> {
@Override
public Task deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
@@ -24,6 +24,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.jetbrains.edu.learning.StudySerializationUtils.Xml.REMOTE_COURSE;
/**
* Implementation of class which contains all the information
* about study in context of current project
@@ -115,10 +117,32 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
if (myCourse == null) {
return null;
}
return serialize();
}
@NotNull
private Element serialize() {
Element el = new Element("taskManager");
Element courseElement = new Element(StudySerializationUtils.Xml.MAIN_ELEMENT);
XmlSerializer.serializeInto(this, courseElement);
el.addContent(courseElement);
Element taskManagerElement = new Element(StudySerializationUtils.Xml.MAIN_ELEMENT);
XmlSerializer.serializeInto(this, taskManagerElement);
if (myCourse instanceof RemoteCourse) {
try {
Element course = new Element(REMOTE_COURSE);
//noinspection RedundantCast
XmlSerializer.serializeInto((RemoteCourse)myCourse, course);
final Element xmlCourse = StudySerializationUtils.Xml.getChildWithName(taskManagerElement, StudySerializationUtils.COURSE);
xmlCourse.removeContent();
xmlCourse.addContent(course);
}
catch (StudySerializationUtils.StudyUnrecognizedFormatException e) {
LOG.error("Failed to serialize remote course");
}
}
el.addContent(taskManagerElement);
return el;
}
@@ -143,7 +167,7 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
//case 5:
// state = StudySerializationUtils.Xml.convertToSixthVersion(state, myProject);
}
XmlSerializer.deserializeInto(this, state.getChild(StudySerializationUtils.Xml.MAIN_ELEMENT));
deserialize(state);
VERSION = CURRENT_VERSION;
if (myCourse != null) {
myCourse.initCourse(true);
@@ -154,6 +178,18 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
}
}
private void deserialize(Element state) throws StudySerializationUtils.StudyUnrecognizedFormatException {
final Element taskManagerElement = state.getChild(StudySerializationUtils.Xml.MAIN_ELEMENT);
XmlSerializer.deserializeInto(this, taskManagerElement);
final Element xmlCourse = StudySerializationUtils.Xml.getChildWithName(taskManagerElement, StudySerializationUtils.COURSE);
final Element remoteCourseElement = xmlCourse.getChild(REMOTE_COURSE);
if (remoteCourseElement != null) {
final RemoteCourse remoteCourse = new RemoteCourse();
XmlSerializer.deserializeInto(remoteCourse, remoteCourseElement);
myCourse = remoteCourse;
}
}
public static StudyTaskManager getInstance(@NotNull final Project project) {
return ServiceManager.getService(project, StudyTaskManager.class);
}
@@ -8,32 +8,29 @@ import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Transient;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.stepic.StepicUser;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
public class Course {
@Expose private List<Lesson> lessons = new ArrayList<>();
@Expose private List<StepicUser> authors = new ArrayList<>();
@Expose private String description;
@Expose private String name;
@Expose private int id;
@Expose @SerializedName("update_date") private Date myUpdateDate;
@Expose private boolean isAdaptive = false;
@Expose @SerializedName("language") private String myLanguage = "Python";
public static final Course INVALID_COURSE = new Course();
@Expose protected List<Lesson> lessons = new ArrayList<>();
transient private List<StepicUser> authors = new ArrayList<>();
@Expose @SerializedName("summary") private String description;
@Expose @SerializedName("title") private String name;
transient private String myLanguage = "Python";
//this field is used to distinguish ordinary and CheckIO projects,
//"PyCharm" is used here for historical reasons
private String courseType = EduNames.PYCHARM;
private String courseMode = EduNames.STUDY; //this field is used to distinguish study and course creator modes
protected String courseMode = EduNames.STUDY; //this field is used to distinguish study and course creator modes
public Course() {}
@@ -136,35 +133,10 @@ public class Course {
}
public boolean isUpToDate() {
if (id == 0) return true;
if (!EduNames.STUDY.equals(courseMode)) return true;
final Date date = EduStepicConnector.getCourseUpdateDate(id);
if (date == null) return true;
if (myUpdateDate == null) return true;
if (date.after(myUpdateDate)) return false;
for (Lesson lesson : lessons) {
if (!lesson.isUpToDate()) return false;
}
return true;
}
public void setUpdated() {
setUpdateDate(EduStepicConnector.getCourseUpdateDate(id));
for (Lesson lesson : lessons) {
lesson.setUpdateDate(EduStepicConnector.getLessonUpdateDate(lesson.getId()));
for (Task task : lesson.getTaskList()) {
task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepId()));
}
}
}
public void setUpdateDate(Date date) {
myUpdateDate = date;
}
public Date getUpdateDate() {
return myUpdateDate;
}
public void setUpdated() {}
public Language getLanguageById() {
return Language.findLanguageByID(getLanguageID());
@@ -209,22 +181,6 @@ public class Course {
this.courseType = courseType;
}
public boolean isAdaptive() {
return isAdaptive;
}
public void setAdaptive(boolean adaptive) {
isAdaptive = adaptive;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCourseMode() {
return courseMode;
}
@@ -235,8 +191,17 @@ public class Course {
public Course copy() {
Element element = XmlSerializer.serialize(this);
Course copy = XmlSerializer.deserialize(element, Course.class);
Course copy = XmlSerializer.deserialize(element, getClass());
copy.initCourse(true);
return copy;
}
public boolean isAdaptive() {
return false;
}
@Override
public String toString() {
return getName();
}
}
@@ -1,175 +0,0 @@
package com.jetbrains.edu.learning.courseFormat;
import com.google.gson.annotations.SerializedName;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.stepic.StepicUser;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Implementation of class which contains information to be shawn in course description in tool window
* and when project is being created
*/
public class CourseInfo {
public static final CourseInfo INVALID_COURSE = new CourseInfo();
@SerializedName("title") private String myName;
int id;
boolean isAdaptive;
@SerializedName("is_public") boolean isPublic;
List<Integer> sections;
List<Integer> instructors = new ArrayList<>();
List<StepicUser> myAuthors = new ArrayList<>();
@SerializedName("summary") private String myDescription;
@SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " Python"; //course type in format "pycharm<version> <language>"
@Nullable private String username;
@SerializedName("update_date") private Date updateDate;
@SerializedName("is_idea_compatible") private boolean isCompatible = true;
public String getName() {
return myName;
}
@NotNull
public List<StepicUser> getAuthors() {
return myAuthors;
}
public String getDescription() {
return myDescription;
}
public String getType() {
return myType;
}
@Override
public String toString() {
return getName();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CourseInfo that = (CourseInfo)o;
final String thatName = that.getName();
final String thatDescription = that.getDescription();
if (thatName == null && getName() == null && thatDescription == null &&
getDescription() == null) {
return true;
}
if (thatName == null || thatDescription == null) {
return false;
}
return thatName.equals(getName())
&& thatDescription.equals(myDescription);
}
@Override
public int hashCode() {
int result = getName() != null ? getName().hashCode() : 0;
result = 31 * result + (myDescription != null ? myDescription.hashCode() : 0);
return result;
}
@Nullable
public String getUsername() {
return username;
}
public void setUsername(@Nullable String username) {
this.username = username;
}
public void setName(String name) {
myName = name;
}
public void setAuthors(List<StepicUser> authors) {
myAuthors = authors;
for (StepicUser author : authors) {
if (author.getId() > 0) {
instructors.add(author.getId());
}
}
}
public void addAuthor(StepicUser author) {
if (myAuthors == null) {
myAuthors = new ArrayList<>();
}
myAuthors.add(author);
}
public void setDescription(String description) {
myDescription = description;
}
public void setType(String type) {
myType = type;
}
public boolean isAdaptive() {
return isAdaptive;
}
public void setAdaptive(boolean adaptive) {
isAdaptive = adaptive;
}
public boolean isPublic() {
return isPublic;
}
public void setPublic(boolean aPublic) {
isPublic = aPublic;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
public static CourseInfo fromCourse(@Nullable final Course course) {
if (course == null) return null;
CourseInfo courseInfo = new CourseInfo();
courseInfo.setName(course.getName());
courseInfo.setDescription(course.getDescription());
courseInfo.setAuthors(course.getAuthors());
return courseInfo;
}
public List<Integer> getSections() {
return sections;
}
public boolean isCompatible() {
return isCompatible;
}
public void setCompatible(boolean compatible) {
isCompatible = compatible;
}
public List<Integer> getInstructors() {
return instructors;
}
}
@@ -0,0 +1,98 @@
package com.jetbrains.edu.learning.courseFormat;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class RemoteCourse extends Course {
//course type in format "pycharm<version> <language>"
@SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " Python";
@SerializedName("is_idea_compatible") private boolean isCompatible = true;
List<Integer> sections;
List<Integer> instructors = new ArrayList<>();
@Expose private int id;
@Expose @SerializedName("update_date") private Date myUpdateDate;
@Expose private boolean isAdaptive = false;
@SerializedName("is_public") boolean isPublic;
public String getType() {
return myType;
}
public List<Integer> getSections() {
return sections;
}
public void setSections(List<Integer> sections) {
this.sections = sections;
}
public void setInstructors(List<Integer> instructors) {
this.instructors = instructors;
}
public List<Integer> getInstructors() {
return instructors;
}
public boolean isUpToDate() {
if (id == 0) return true;
if (!EduNames.STUDY.equals(courseMode)) return true;
final Date date = EduStepicConnector.getCourseUpdateDate(id);
if (date == null) return true;
if (myUpdateDate == null) return true;
if (date.after(myUpdateDate)) return false;
for (Lesson lesson : lessons) {
if (!lesson.isUpToDate()) return false;
}
return true;
}
public void setUpdated() {
setUpdateDate(EduStepicConnector.getCourseUpdateDate(id));
for (Lesson lesson : lessons) {
lesson.setUpdateDate(EduStepicConnector.getLessonUpdateDate(lesson.getId()));
for (Task task : lesson.getTaskList()) {
task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepId()));
}
}
}
public void setUpdateDate(Date date) {
myUpdateDate = date;
}
public Date getUpdateDate() {
return myUpdateDate;
}
public boolean isAdaptive() {
return isAdaptive;
}
public void setAdaptive(boolean adaptive) {
isAdaptive = adaptive;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void copyCourseParameters(RemoteCourse course) {
setName(course.getName());
setUpdateDate(course.getUpdateDate());
}
}
@@ -23,7 +23,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.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
@@ -41,12 +41,11 @@ import static com.jetbrains.edu.learning.StudyUtils.execCancelable;
public class StudyProjectGenerator {
private static final Logger LOG = Logger.getInstance(StudyProjectGenerator.class.getName());
private final List<SettingsListener> myListeners = ContainerUtil.newArrayList();
private List<CourseInfo> myCourses = new ArrayList<>();
private List<Course> myLocalCourses = new ArrayList<>();
private List<Course> myCourses = new ArrayList<>();
private List<Integer> myEnrolledCoursesIds = new ArrayList<>();
protected CourseInfo mySelectedCourseInfo;
protected Course mySelectedCourse;
public void setCourses(List<CourseInfo> courses) {
public void setCourses(List<Course> courses) {
myCourses = courses;
}
@@ -64,12 +63,12 @@ public class StudyProjectGenerator {
return myEnrolledCoursesIds;
}
public void setSelectedCourse(@NotNull final CourseInfo courseName) {
mySelectedCourseInfo = courseName;
public void setSelectedCourse(@NotNull final Course course) {
mySelectedCourse = course;
}
public CourseInfo getSelectedCourseInfo() {
return mySelectedCourseInfo;
public Course getSelectedCourse() {
return mySelectedCourse;
}
public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir) {
@@ -94,22 +93,18 @@ public class StudyProjectGenerator {
@Nullable
public Course getCourse(@NotNull final Project project) {
final CourseInfo info = mySelectedCourseInfo;
for (Course course : myLocalCourses) {
if (course.getName().equals(info.getName())){
course.initCourse(false);
return course;
}
if (mySelectedCourse instanceof RemoteCourse) {
return getCourseFromStepic(project, (RemoteCourse)mySelectedCourse);
}
return getCourseFromStepic(project);
mySelectedCourse.initCourse(false);
return mySelectedCourse;
}
private Course getCourseFromStepic(@NotNull Project project) {
private static RemoteCourse getCourseFromStepic(@NotNull Project project, RemoteCourse selectedCourse) {
return ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
return execCancelable(() -> {
final Course course = EduStepicConnector.getCourse(project, mySelectedCourseInfo);
final RemoteCourse course = EduStepicConnector.getCourse(project, selectedCourse);
if (StudyUtils.isCourseValid(course)) {
course.initCourse(false);
}
@@ -119,25 +114,25 @@ public class StudyProjectGenerator {
}
// Supposed to be called under progress
public List<CourseInfo> getCourses(boolean force) {
public List<Course> getCourses(boolean force) {
if (force) {
myCourses = execCancelable(() -> EduStepicConnector.getCourses(StepicUpdateSettings.getInstance().getUser()));
}
if (myCourses == null || myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(CourseInfo.INVALID_COURSE))) {
if (myCourses == null || myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(Course.INVALID_COURSE))) {
myCourses = Collections.singletonList(getBundledIntro());
}
sortCourses(myCourses);
return myCourses;
}
public void sortCourses(List<CourseInfo> result) {
public void sortCourses(List<Course> result) {
// sort courses so as to have non-adaptive courses in the beginning of the list
Collections.sort(result, (c1, c2) -> {
if (mySelectedCourseInfo != null) {
if (mySelectedCourseInfo.equals(c1)) {
if (mySelectedCourse != null) {
if (mySelectedCourse.equals(c1)) {
return -1;
}
if (mySelectedCourseInfo.equals(c2)) {
if (mySelectedCourse.equals(c2)) {
return 1;
}
}
@@ -149,7 +144,7 @@ public class StudyProjectGenerator {
}
@NotNull
public List<CourseInfo> getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @NotNull final Project project) {
public List<Course> getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @NotNull final Project project) {
try {
return ProgressManager.getInstance()
.runProcessWithProgressSynchronously(() -> {
@@ -158,7 +153,7 @@ public class StudyProjectGenerator {
}, progressTitle, true, project);
}
catch (RuntimeException e) {
return Collections.singletonList(CourseInfo.INVALID_COURSE);
return Collections.singletonList(Course.INVALID_COURSE);
}
}
@@ -177,21 +172,21 @@ public class StudyProjectGenerator {
}
@Nullable
public CourseInfo getBundledIntro() {
public Course getBundledIntro() {
final LanguageExtensionPoint<EduPluginConfigurator>[] extensions = Extensions.getExtensions(EduPluginConfigurator.EP_NAME, null);
for (LanguageExtensionPoint<EduPluginConfigurator> extension : extensions) {
final EduPluginConfigurator configurator = extension.getInstance();
final String path = configurator.getBundledCoursePath();
if (path != null) {
return getCourseInfo(path);
return getCourse(path);
}
}
return null;
}
@Nullable
public CourseInfo addLocalCourse(String zipFilePath) {
final CourseInfo courseInfo = getCourseInfo(zipFilePath);
public Course addLocalCourse(String zipFilePath) {
final Course courseInfo = getCourse(zipFilePath);
if (courseInfo != null) {
myCourses.add(0, courseInfo);
}
@@ -199,22 +194,17 @@ public class StudyProjectGenerator {
}
@Nullable
public CourseInfo getCourseInfo(String zipFilePath) {
public Course getCourse(String zipFilePath) {
try {
final JBZipFile zipFile = new JBZipFile(zipFilePath);
final JBZipEntry entry = zipFile.getEntry(EduNames.COURSE_META_FILE);
byte[] bytes = entry.getData();
final String jsonText = new String(bytes, CharsetToolkit.UTF8_CHARSET);
Gson gson = new GsonBuilder()
.registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskDeserializer())
.registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskAdapter())
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
Course course = gson.fromJson(jsonText, Course.class);
final CourseInfo courseInfo = CourseInfo.fromCourse(course);
if (courseInfo != null) {
myLocalCourses.add(course);
}
return courseInfo;
return gson.fromJson(jsonText, Course.class);
}
catch (IOException e) {
LOG.error("Failed to unzip course archive");
@@ -14,7 +14,6 @@ import com.intellij.openapi.util.InvalidDataException;
import com.jetbrains.edu.learning.EduPluginConfigurator;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator;
import org.jdom.JDOMException;
@@ -27,7 +26,7 @@ import java.io.IOException;
class EduCustomCourseModuleBuilder extends JavaModuleBuilder {
private static final Logger LOG = Logger.getInstance(EduCustomCourseModuleBuilder.class);
private EduProjectGenerator myGenerator = new EduProjectGenerator();
private CourseInfo mySelectedCourse;
private Course mySelectedCourse;
@Nullable
@Override
@@ -36,8 +35,7 @@ class EduCustomCourseModuleBuilder extends JavaModuleBuilder {
if (module == null) {
return null;
}
String type = mySelectedCourse.getType();
String languageName = type.substring("pycharm ".length());
String languageName = mySelectedCourse.getLanguageID();
Language language = Language.findLanguageByID(languageName);
if (language == null) {
return module;
@@ -87,7 +85,7 @@ class EduCustomCourseModuleBuilder extends JavaModuleBuilder {
return ProjectWizardStepFactory.getInstance().createJavaSettingsStep(settingsStep, this, Conditions.alwaysTrue());
}
void setSelectedCourse(CourseInfo selectedCourse) {
void setSelectedCourse(Course selectedCourse) {
mySelectedCourse = selectedCourse;
}
@@ -97,8 +95,7 @@ class EduCustomCourseModuleBuilder extends JavaModuleBuilder {
throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
Module baseModule = super.createModule(moduleModel);
if (mySelectedCourse != null) {
String type = mySelectedCourse.getType();
String languageName = type.substring("pycharm ".length());
String languageName = mySelectedCourse.getLanguageID();
Language language = Language.findLanguageByID(languageName);
if (language != null) {
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language);
@@ -2,7 +2,7 @@ package com.jetbrains.edu.learning.intellij.localCourses;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.ui.StudyNewProjectPanel;
@@ -25,7 +25,7 @@ public class EduLocalCoursePanel {
@Override
public void actionPerformed(ActionEvent actionEvent) {
String coursePath = myCourseArchivePath.getText();
CourseInfo courseInfo = generator.addLocalCourse(coursePath);
Course courseInfo = generator.addLocalCourse(coursePath);
if (courseInfo != null) {
myBuilder.setSelectedCourse(courseInfo);
myGenerator.setSelectedCourse(courseInfo);
@@ -67,11 +67,12 @@ public class EduAdaptiveStepicConnector {
private static final Logger LOG = Logger.getInstance(EduAdaptiveStepicConnector.class);
private static final int CONNECTION_TIMEOUT = 60 * 1000;
private static final String CODE_TASK_TYPE = "code";
private static final String CODE_TASK_NAME = "code.py";
private static final String CHOICE_TYPE_TEXT = "choice";
private static final String TEXT_STEP_TYPE = "text";
@Nullable
public static Task getNextRecommendation(@NotNull Project project, @NotNull Course course) {
public static Task getNextRecommendation(@NotNull Project project, @NotNull RemoteCourse course) {
try {
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient();
if (client == null) {
@@ -204,8 +205,8 @@ public class EduAdaptiveStepicConnector {
private static void createMockTaskFile(@NotNull Task task, String editorText) {
final TaskFile taskFile = new TaskFile();
taskFile.text = editorText;
taskFile.name = "code.py";
task.taskFiles.put("code.py", taskFile);
taskFile.name = CODE_TASK_NAME;
task.taskFiles.put(CODE_TASK_NAME, taskFile);
}
@Nullable
@@ -297,7 +298,7 @@ public class EduAdaptiveStepicConnector {
int reaction) {
final StudyEditor editor = StudyUtils.getSelectedStudyEditor(project);
final Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null || editor == null || editor.getTaskFile() == null) {
if (course == null || editor == null || editor.getTaskFile() == null || !(course instanceof RemoteCourse)) {
return;
}
indicator.checkCanceled();
@@ -314,7 +315,7 @@ public class EduAdaptiveStepicConnector {
String.valueOf(user.getId()), reaction);
if (recommendationReaction) {
indicator.checkCanceled();
final Task task = getNextRecommendation(project, course);
final Task task = getNextRecommendation(project, (RemoteCourse)course);
if (task != null) {
task.initTask(lesson, false);
@@ -438,11 +439,9 @@ public class EduAdaptiveStepicConnector {
}
}
else {
final TaskFile taskFile = new TaskFile();
taskFile.name = CODE_TASK_TYPE;
final String templateForTask = getCodeTemplateForTask(project, task, step.options.codeTemplates);
taskFile.text = templateForTask == null ? "# write your answer here \n" : templateForTask;
task.taskFiles.put("code.py", taskFile);
String text = templateForTask == null ? "# write your answer here \n" : templateForTask;
createMockTaskFile(task, text);
}
return task;
}
@@ -662,11 +661,11 @@ public class EduAdaptiveStepicConnector {
public static List<Integer> getEnrolledCoursesIds(@NotNull StepicUser stepicUser) {
try {
final URI enrolledCoursesUri = new URIBuilder(EduStepicNames.COURSES).addParameter("enrolled", "true").build();
final List<CourseInfo> courses = EduStepicAuthorizedClient.getFromStepic(enrolledCoursesUri.toString(),
final List<RemoteCourse> courses = EduStepicAuthorizedClient.getFromStepic(enrolledCoursesUri.toString(),
StepicWrappers.CoursesContainer.class,
stepicUser).courses;
final ArrayList<Integer> ids = new ArrayList<>();
for (CourseInfo course : courses) {
for (RemoteCourse course : courses) {
ids.add(course.getId());
}
return ids;
@@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
@@ -70,6 +71,7 @@ public class EduStepicClient {
static <T> T deserializeStepicResponse(Class<T> container, String responseString) {
Gson gson = new GsonBuilder().registerTypeAdapter(StepicWrappers.StepOptions.class, new StudySerializationUtils.Json.StepicStepOptionsAdapter()).
excludeFieldsWithModifiers(Modifier.TRANSIENT).
setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").
setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
return gson.fromJson(responseString, container);
@@ -12,8 +12,8 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.learning.StudySettings;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
@@ -65,9 +65,9 @@ public class EduStepicConnector {
}
@NotNull
public static List<CourseInfo> getCourses(@Nullable StepicUser user) {
public static List<Course> getCourses(@Nullable StepicUser user) {
try {
List<CourseInfo> result = new ArrayList<>();
List<Course> result = new ArrayList<>();
int pageNumber = 1;
while (addCoursesFromStepic(user, result, pageNumber)) {
pageNumber += 1;
@@ -77,13 +77,13 @@ public class EduStepicConnector {
catch (IOException e) {
LOG.warn("Cannot load course list " + e.getMessage());
}
return Collections.singletonList(CourseInfo.INVALID_COURSE);
return Collections.emptyList();
}
public static Date getCourseUpdateDate(final int courseId) {
final String url = EduStepicNames.COURSES + "/" + courseId;
try {
final List<CourseInfo> courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses;
final List<RemoteCourse> courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses;
if (!courses.isEmpty()) {
return courses.get(0).getUpdateDate();
}
@@ -125,7 +125,7 @@ public class EduStepicConnector {
return null;
}
private static boolean addCoursesFromStepic(@Nullable StepicUser user, List<CourseInfo> result, int pageNumber) throws IOException {
private static boolean addCoursesFromStepic(@Nullable StepicUser user, List<Course> result, int pageNumber) throws IOException {
final URI url;
try {
url = new URIBuilder(EduStepicNames.COURSES).addParameter("is_idea_compatible", "true").
@@ -146,27 +146,28 @@ public class EduStepicConnector {
return coursesContainer.meta.containsKey("has_next") && coursesContainer.meta.get("has_next") == Boolean.TRUE;
}
static void addAvailableCourses(List<CourseInfo> result, StepicWrappers.CoursesContainer coursesContainer) throws IOException {
final List<CourseInfo> courseInfos = coursesContainer.courses;
for (CourseInfo info : courseInfos) {
static void addAvailableCourses(List<Course> result, StepicWrappers.CoursesContainer coursesContainer) throws IOException {
final List<RemoteCourse> courses = coursesContainer.courses;
for (RemoteCourse info : courses) {
if (!info.isAdaptive() && StringUtil.isEmptyOrSpaces(info.getType())) continue;
if (canBeOpened(info)) {
final ArrayList<StepicUser> authors = new ArrayList<>();
for (Integer instructor : info.getInstructors()) {
final StepicUser author = EduStepicClient.getFromStepic(EduStepicNames.USERS + String.valueOf(instructor),
StepicWrappers.AuthorWrapper.class).users.get(0);
info.addAuthor(author);
authors.add(author);
}
info.setAuthors(authors);
if (info.isAdaptive()) {
info.setDescription("This is a Stepik Adaptive course.\n\n" + info.getDescription() + ADAPTIVE_NOTE);
}
result.add(info);
}
}
}
static boolean canBeOpened(CourseInfo courseInfo) {
static boolean canBeOpened(RemoteCourse courseInfo) {
if (courseInfo.isAdaptive()) {
return true;
}
@@ -190,26 +191,19 @@ public class EduStepicConnector {
}
}
public static Course getCourse(@NotNull final Project project, @NotNull final CourseInfo info) {
final Course course = new Course();
course.setAuthors(info.getAuthors());
course.setDescription(info.getDescription());
course.setAdaptive(info.isAdaptive());
course.setId(info.getId());
course.setUpdateDate(getCourseUpdateDate(info.getId()));
if (!course.isAdaptive()) {
String courseType = info.getType();
course.setName(info.getName());
public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse course) {
final RemoteCourse remoteCourse = (RemoteCourse)course.copy();
if (!remoteCourse.isAdaptive()) {
String courseType = remoteCourse.getType();
final int separator = courseType.indexOf(" ");
assert separator != -1;
final String language = courseType.substring(separator + 1);
course.setLanguage(language);
remoteCourse.setLanguage(language);
try {
for (Integer section : info.getSections()) {
course.addLessons(getLessons(section));
for (Integer section : remoteCourse.getSections()) {
remoteCourse.addLessons(getLessons(section));
}
return course;
return remoteCourse;
}
catch (IOException e) {
LOG.error("IOException " + e.getMessage());
@@ -218,14 +212,13 @@ public class EduStepicConnector {
else {
final Lesson lesson = new Lesson();
lesson.setName("Adaptive");
course.addLesson(lesson);
course.setName(info.getName());
remoteCourse.addLesson(lesson);
//TODO: more specific name?
final Task recommendation = EduAdaptiveStepicConnector.getNextRecommendation(project, course);
final Task recommendation = EduAdaptiveStepicConnector.getNextRecommendation(project, remoteCourse);
if (recommendation != null) {
lesson.addTask(recommendation);
}
return course;
return remoteCourse;
}
return null;
}
@@ -11,7 +11,8 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Alarm;
import com.intellij.util.text.DateFormatUtil;
import com.jetbrains.edu.learning.StudySettings;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -50,13 +51,14 @@ public class EduStepicUpdater {
private static ActionCallback updateCourseList() {
ActionCallback callback = new ActionCallback();
ApplicationManager.getApplication().executeOnPooledThread(() -> {
final List<CourseInfo> courses = EduStepicConnector.getCourses(null);
StudySettings.getInstance().setLastTimeChecked(System.currentTimeMillis());
final List<Course> courses = EduStepicConnector.getCourses(null);
StepicSettings.getInstance().setLastTimeChecked(System.currentTimeMillis());
if (!courses.isEmpty()) {
List<CourseInfo> updated = new ArrayList<>();
for (CourseInfo course : courses) {
if (course.getUpdateDate().after(new Date(StepicUpdateSettings.getInstance().getLastTimeChecked()))) {
List<Course> updated = new ArrayList<>();
for (Course course : courses) {
if (course instanceof RemoteCourse && ((RemoteCourse)course).getUpdateDate().
after(new Date(StepicUpdateSettings.getInstance().getLastTimeChecked()))) {
updated.add(course);
}
}
@@ -69,7 +71,7 @@ public class EduStepicUpdater {
}
else {
title = "New courses available";
message = StringUtil.join(updated, CourseInfo::getName, ", ");
message = StringUtil.join(updated, Course::getName, ", ");
}
final Notification notification = new Notification("New.course", title, message, NotificationType.INFORMATION);
notification.notify(null);
@@ -11,8 +11,8 @@ 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.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
@@ -171,7 +171,7 @@ public class StepicWrappers {
}
public static class CoursesContainer {
public List<CourseInfo> courses;
public List<RemoteCourse> courses;
public Map meta;
}
@@ -185,10 +185,10 @@ public class StepicWrappers {
}
public static class CourseWrapper {
CourseInfo course;
RemoteCourse course;
public CourseWrapper(Course course) {
this.course = new CourseInfo();
this.course = new RemoteCourse();
this.course.setName(course.getName());
this.course.setDescription(course.getDescription());
this.course.setAuthors(course.getAuthors());
@@ -29,7 +29,6 @@ import com.intellij.util.ui.UIUtil;
import com.jetbrains.edu.learning.StudySettings;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient;
@@ -52,9 +51,9 @@ import java.util.List;
* data: 7/31/14.
*/
public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
private List<CourseInfo> myAvailableCourses = new ArrayList<>();
private List<Course> myAvailableCourses = new ArrayList<>();
private JButton myBrowseButton;
private ComboBox<CourseInfo> myCoursesComboBox;
private ComboBox<Course> myCoursesComboBox;
private JButton myRefreshButton;
private JLabel myAuthorLabel;
private JPanel myInfoPanel;
@@ -122,20 +121,20 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
private void initCoursesCombobox() {
myAvailableCourses =
myGenerator.getCoursesUnderProgress(!isLocal, "Getting Available Courses", ProjectManager.getInstance().getDefaultProject());
if (myAvailableCourses.contains(CourseInfo.INVALID_COURSE)) {
if (myAvailableCourses.contains(Course.INVALID_COURSE)) {
setError(CONNECTION_ERROR);
}
else {
addCoursesToCombobox(myAvailableCourses);
final CourseInfo selectedCourse = StudyUtils.getFirst(myAvailableCourses);
final Course selectedCourse = StudyUtils.getFirst(myAvailableCourses);
if (selectedCourse == null) return;
setAuthors(selectedCourse);
myDescriptionPane.setText(selectedCourse.getDescription());
myDescriptionPane.setEditable(false);
//setting the first course in list as selected
myGenerator.setSelectedCourse(selectedCourse);
if (myGenerator.getSelectedCourseInfo() != null) {
myCoursesComboBox.setSelectedItem(myGenerator.getSelectedCourseInfo());
if (myGenerator.getSelectedCourse() != null) {
myCoursesComboBox.setSelectedItem(myGenerator.getSelectedCourse());
}
if (selectedCourse.isAdaptive() && !myGenerator.isLoggedIn()) {
setError(LOGIN_TO_STEPIC_MESSAGE);
@@ -174,7 +173,7 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
file -> {
String fileName = file.getPath();
int oldSize = myAvailableCourses.size();
CourseInfo courseInfo = myGenerator.addLocalCourse(fileName);
Course courseInfo = myGenerator.addLocalCourse(fileName);
if (courseInfo != null) {
if (oldSize != myAvailableCourses.size()) {
myCoursesComboBox.addItem(courseInfo);
@@ -185,11 +184,11 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
else {
setError(INVALID_COURSE);
myCoursesComboBox.removeAllItems();
myCoursesComboBox.addItem(CourseInfo.INVALID_COURSE);
for (CourseInfo course : myAvailableCourses) {
myCoursesComboBox.addItem(Course.INVALID_COURSE);
for (Course course : myAvailableCourses) {
myCoursesComboBox.addItem(course);
}
myCoursesComboBox.setSelectedItem(CourseInfo.INVALID_COURSE);
myCoursesComboBox.setSelectedItem(Course.INVALID_COURSE);
}
});
}
@@ -221,8 +220,8 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
isComboboxInitialized = true;
initCoursesCombobox();
}
CourseInfo selectedCourse = (CourseInfo)myCoursesComboBox.getSelectedItem();
if (selectedCourse == null || selectedCourse.equals(CourseInfo.INVALID_COURSE)) {
Course selectedCourse = (Course)myCoursesComboBox.getSelectedItem();
if (selectedCourse == null || selectedCourse.equals(Course.INVALID_COURSE)) {
setError(CONNECTION_ERROR);
}
}
@@ -256,9 +255,9 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
myAnchor = anchor;
}
public void updateInfoPanel(@NotNull CourseInfo selectedCourseInfo) {
setAuthors(selectedCourseInfo);
myDescriptionPane.setText(selectedCourseInfo.getDescription());
public void updateInfoPanel(@NotNull Course selectedCourse) {
setAuthors(selectedCourse);
myDescriptionPane.setText(selectedCourse.getDescription());
}
/**
@@ -269,15 +268,15 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
private class RefreshActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
final List<CourseInfo> courses =
final List<Course> courses =
myGenerator.getCoursesUnderProgress(true, "Refreshing Course List", DefaultProjectFactory.getInstance().getDefaultProject());
if (!courses.contains(CourseInfo.INVALID_COURSE)) {
if (!courses.contains(Course.INVALID_COURSE)) {
refreshCoursesList(courses);
}
}
}
private void refreshCoursesList(@NotNull final List<CourseInfo> courses) {
private void refreshCoursesList(@NotNull final List<Course> courses) {
if (courses.isEmpty()) {
setError(CONNECTION_ERROR);
return;
@@ -285,7 +284,7 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
myCoursesComboBox.removeAllItems();
addCoursesToCombobox(courses);
final CourseInfo selectedCourse = StudyUtils.getFirst(courses);
final Course selectedCourse = StudyUtils.getFirst(courses);
if (selectedCourse == null) return;
myGenerator.setSelectedCourse(selectedCourse);
@@ -293,8 +292,8 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
myAvailableCourses = courses;
}
private void addCoursesToCombobox(@NotNull List<CourseInfo> courses) {
for (CourseInfo courseInfo : courses) {
private void addCoursesToCombobox(@NotNull List<Course> courses) {
for (Course courseInfo : courses) {
myCoursesComboBox.addItem(courseInfo);
}
}
@@ -309,15 +308,15 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
@Override
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox)e.getSource();
CourseInfo selectedCourse = (CourseInfo)cb.getSelectedItem();
if (selectedCourse == null || selectedCourse.equals(CourseInfo.INVALID_COURSE)) {
Course selectedCourse = (Course)cb.getSelectedItem();
if (selectedCourse == null || selectedCourse.equals(Course.INVALID_COURSE)) {
myAuthorLabel.setText("");
myDescriptionPane.setText("");
setError(INVALID_COURSE);
return;
}
updateInfoPanel(selectedCourse);
myCoursesComboBox.removeItem(CourseInfo.INVALID_COURSE);
myCoursesComboBox.removeItem(Course.INVALID_COURSE);
myGenerator.setSelectedCourse(selectedCourse);
setOK();
@@ -329,12 +328,12 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
}
}
private void setAuthors(CourseInfo selectedCourse) {
private void setAuthors(Course selectedCourse) {
final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors());
myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : "");
}
public JComboBox<CourseInfo> getCoursesComboBox() {
public JComboBox<Course> getCoursesComboBox() {
return myCoursesComboBox;
}
@@ -367,7 +366,7 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
stepicUser.setPassword(myLoginPanel.getPassword());
StudySettings.getInstance().setUser(stepicUser);
myGenerator.setEnrolledCoursesIds(EduAdaptiveStepicConnector.getEnrolledCoursesIds(stepicUser));
final List<CourseInfo> courses = myGenerator.getCourses(true);
final List<Course> courses = myGenerator.getCourses(true);
if (courses != null && myRefreshCourseList) {
ApplicationManager.getApplication().invokeLater(() -> refreshCoursesList(courses));
}
@@ -8,7 +8,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
@@ -65,9 +65,9 @@ public class StudyStepicFormatTest {
EduStepicClient.deserializeStepicResponse(StepicWrappers.CoursesContainer.class, responseString);
assertNotNull(container.courses);
assertTrue("Incorrect number of courses", container.courses.size() == 4);
List<CourseInfo> filtered = ContainerUtil.filter(container.courses, info -> EduStepicConnector.canBeOpened(info));
List<RemoteCourse> filtered = ContainerUtil.filter(container.courses, info -> EduStepicConnector.canBeOpened(info));
assertEquals(ContainerUtil.newArrayList("Adaptive Python", "Introduction to Python", "format2"),
ContainerUtil.map(filtered, CourseInfo::getName));
ContainerUtil.map(filtered, RemoteCourse::getName));
}
@Test
@@ -10,7 +10,6 @@ import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.python.newProject.steps.PyCharmNewProjectStep;
import org.jetbrains.annotations.NotNull;
@@ -44,10 +43,10 @@ class CreateFromArchiveProjectStep extends PyCharmNewProjectStep {
@NotNull
@Override
protected DirectoryProjectGenerator createEmptyProjectGenerator() {
Course course = StudyTaskManager.getInstance(myProject).getCourse();
if (course != null) {
Course currentCourse = StudyTaskManager.getInstance(myProject).getCourse();
if (currentCourse != null) {
VirtualFile folder = CCUtils.getGeneratedFilesFolder(myProject, myModule);
String zipName = FileUtil.sanitizeFileName(course.getName());
String zipName = FileUtil.sanitizeFileName(currentCourse.getName());
if (zipName.isEmpty()) {
zipName = EduNames.COURSE;
}
@@ -55,9 +54,9 @@ class CreateFromArchiveProjectStep extends PyCharmNewProjectStep {
CCCreateCourseArchive.createCourseArchive(myProject, myModule, zipName, locationDir, false);
String path = FileUtil.join(FileUtil.toSystemDependentName(folder.getPath()), zipName + ".zip");
StudyProjectGenerator generator = myGenerator.getGenerator();
CourseInfo info = generator.addLocalCourse(path);
assert info != null;
generator.setSelectedCourse(info);
Course course = generator.addLocalCourse(path);
assert course != null;
generator.setSelectedCourse(course);
}
return myGenerator;
}
@@ -25,7 +25,7 @@ import com.intellij.psi.PsiManager;
import com.intellij.util.BooleanFunction;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.ui.StudyNewProjectPanel;
@@ -87,7 +87,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
@Override
public void mouseClicked(MouseEvent e) {
final Object selectedItem = mySettingsPanel.getCoursesComboBox().getSelectedItem();
if (selectedItem != null && ((CourseInfo)selectedItem).isAdaptive() && !myGenerator.isLoggedIn()) {
if (selectedItem != null && ((Course)selectedItem).isAdaptive() && !myGenerator.isLoggedIn()) {
mySettingsPanel.showLoginDialog(false, "Signing In");
}
}
@@ -95,14 +95,14 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
@Override
public void mouseEntered(MouseEvent e) {
final Object selectedItem = mySettingsPanel.getCoursesComboBox().getSelectedItem();
if (selectedItem != null && ((CourseInfo)selectedItem).isAdaptive() && !myGenerator.isLoggedIn()) {
if (selectedItem != null && ((Course)selectedItem).isAdaptive() && !myGenerator.isLoggedIn()) {
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
}
@Override
public void mouseExited(MouseEvent e) {
final CourseInfo selectedItem = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem();
final Course selectedItem = (Course)mySettingsPanel.getCoursesComboBox().getSelectedItem();
if (selectedItem != null && selectedItem.isAdaptive() && !myGenerator.isLoggedIn()) {
e.getComponent().setCursor(Cursor.getDefaultCursor());
}
@@ -165,7 +165,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
return mySettingsPanel;
}
public void setSelectedCourse(CourseInfo course) {
public void setSelectedCourse(Course course) {
myGenerator.setSelectedCourse(course);
}
@@ -183,13 +183,13 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
public BooleanFunction<PythonProjectGenerator> beforeProjectGenerated(@Nullable Sdk sdk) {
return generator -> {
final List<Integer> enrolledCoursesIds = myGenerator.getEnrolledCoursesIds();
final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem();
if (course == null) return true;
if (course.getId() > 0 && !enrolledCoursesIds.contains(course.getId())) {
final Course course = (Course)mySettingsPanel.getCoursesComboBox().getSelectedItem();
if (course == null || !(course instanceof RemoteCourse)) return true;
if (((RemoteCourse)course).getId() > 0 && !enrolledCoursesIds.contains(((RemoteCourse)course).getId())) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(),
StudySettings.getInstance().getUser()));
return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(((RemoteCourse)course).getId(),
StepicSettings.getInstance().getUser()));
}, "Creating Course", true, ProjectManager.getInstance().getDefaultProject());
}
@@ -21,7 +21,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.jetbrains.edu.learning.EduPluginConfigurator;
import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.newProject.steps.ProjectSpecificSettingsStep;
import com.jetbrains.python.newProject.steps.PythonGenerateProjectCallback;
@@ -64,7 +64,7 @@ public class PyStudyIntroductionCourseAction extends AnAction {
final PyStudyDirectoryProjectGenerator generator = new PyStudyDirectoryProjectGenerator(true);
final EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(PythonLanguage.getInstance());
final String bundledCoursePath = configurator.getBundledCoursePath();
CourseInfo introCourse = generator.getGenerator().getCourseInfo(bundledCoursePath);
Course introCourse = generator.getGenerator().getCourse(bundledCoursePath);
if (introCourse == null) {
return;
}