package com.jetbrains.edu.coursecreator;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.AbstractProjectComponent;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.jetbrains.edu.learning.StudyProjectComponent;
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.learning.courseFormat.Task;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
import org.jetbrains.annotations.NotNull;
import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
public class CCProjectComponent extends AbstractProjectComponent {
+ private static final Logger LOG = Logger.getInstance(CCProjectComponent.class);
private final CCVirtualFileListener myTaskFileLifeListener = new CCVirtualFileListener();
private final Project myProject;
myProject = project;
}
- public void initComponent() {
- VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener);
- }
-
public void migrateIfNeeded() {
Course studyCourse = StudyTaskManager.getInstance(myProject).getCourse();
- Course course = CCProjectService.getInstance(myProject).getCourse();
- if (studyCourse == null && course != null) {
- course.setCourseMode(CCUtils.COURSE_MODE);
+ if (studyCourse == null) {
+ Course oldCourse = CCProjectService.getInstance(myProject).getCourse();
+ if (oldCourse == null) {
+ return;
+ }
+ StudyTaskManager.getInstance(myProject).setCourse(oldCourse);
+ CCProjectService.getInstance(myProject).setCourse(null);
+ oldCourse.initCourse(true);
+ oldCourse.setCourseMode(CCUtils.COURSE_MODE);
File coursesDir = new File(PathManager.getConfigPath(), "courses");
- File courseDir = new File(coursesDir, course.getName() + "-" + myProject.getName());
- course.setCourseDirectory(courseDir.getPath());
- StudyTaskManager.getInstance(myProject).setCourse(course);
- StudyProjectComponent.getInstance(myProject).registerStudyToolWindow(course);
+ File courseDir = new File(coursesDir, oldCourse.getName() + "-" + myProject.getName());
+ oldCourse.setCourseDirectory(courseDir.getPath());
+ StudyProjectComponent.getInstance(myProject).registerStudyToolWindow(oldCourse);
+ transformFiles(oldCourse, myProject);
+ }
+ }
+
+ private static void transformFiles(Course course, Project project) {
+ List<VirtualFile> files = getAllAnswerTaskFiles(course, project);
+ for (VirtualFile answerFile : files) {
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ String answerName = answerFile.getName();
+ String name = FileUtil.getNameWithoutExtension(FileUtil.getNameWithoutExtension(answerName)) + "." + FileUtilRt.getExtension(answerName);
+ VirtualFile file = answerFile.getParent().findChild(name);
+ try {
+ if (file != null) {
+ file.delete(CCProjectComponent.class);
+ }
+ answerFile.rename(CCProjectComponent.class, name);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ });
+ }
+ }
+
+
+ private static List<VirtualFile> getAllAnswerTaskFiles(@NotNull Course course, @NotNull Project project) {
+ List<VirtualFile> result = new ArrayList<>();
+ for (Lesson lesson : course.getLessons()) {
+ for (Task task : lesson.getTaskList()) {
+ for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ String name = entry.getKey();
+ String answerName = FileUtil.getNameWithoutExtension(name) + CCUtils.ANSWER_EXTENSION_DOTTED + FileUtilRt.getExtension(name);
+ String taskPath = FileUtil.join(project.getBasePath(), EduNames.LESSON + lesson.getIndex(), EduNames.TASK + task.getIndex());
+ VirtualFile taskFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.join(taskPath, answerName));
+ if (taskFile == null) {
+ taskFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.join(taskPath, EduNames.SRC, answerName));
+ }
+ if (taskFile!= null) {
+ result.add(taskFile);
+ }
+ }
+ }
}
+ return result;
}
@NotNull
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
-import com.intellij.util.xmlb.XmlSerializerUtil;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.util.xmlb.XmlSerializer;
+import com.intellij.util.xmlb.annotations.Transient;
+import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
+import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
+import java.util.Map;
+
+import static com.jetbrains.edu.learning.StudySerializationUtils.*;
+import static com.jetbrains.edu.learning.StudySerializationUtils.Xml.*;
+
+/**
+ * @deprecated since version 3
+ */
@State(name = "CCProjectService", storages = @Storage("course_service.xml"))
-public class CCProjectService implements PersistentStateComponent<CCProjectService> {
+public class CCProjectService implements PersistentStateComponent<Element> {
private Course myCourse;
+ @Transient private final Project myProject;
+
+ public CCProjectService() {
+ this(null);
+ }
+
+ public CCProjectService(Project project) {
+ myProject = project;
+ }
public Course getCourse() {
return myCourse;
}
@Override
- public CCProjectService getState() {
- return this;
+ public Element getState() {
+ return XmlSerializer.serialize(this);
}
@Override
- public void loadState(CCProjectService state) {
- XmlSerializerUtil.copyBean(state, this);
- myCourse.initCourse(true);
+ public void loadState(Element state) {
+ Element courseElement = getChildWithName(state, COURSE).getChild(COURSE_TITLED);
+ for (Element lesson : getChildList(courseElement, LESSONS)) {
+ int lessonIndex = getAsInt(lesson, INDEX);
+ for (Element task : getChildList(lesson, TASK_LIST)) {
+ int taskIndex = getAsInt(task, INDEX);
+ Map<String, Element> taskFiles = getChildMap(task, TASK_FILES);
+ for (Map.Entry<String, Element> entry : taskFiles.entrySet()) {
+ Element taskFileElement = entry.getValue();
+ String name = entry.getKey();
+ String answerName = FileUtil.getNameWithoutExtension(name) + CCUtils.ANSWER_EXTENSION_DOTTED + FileUtilRt.getExtension(name);
+ Document document = StudyUtils.getDocument(myProject.getBasePath(), lessonIndex, taskIndex, answerName);
+ if (document == null) {
+ continue;
+ }
+ for (Element placeholder : getChildList(taskFileElement, ANSWER_PLACEHOLDERS)) {
+ Element lineElement = getChildWithName(placeholder, LINE);
+ int line = lineElement != null ? Integer.valueOf(lineElement.getAttributeValue(VALUE)) : 0;
+ Element startElement = getChildWithName(placeholder, START);
+ int start = startElement != null ? Integer.valueOf(startElement.getAttributeValue(VALUE)) : 0;
+ int offset = document.getLineStartOffset(line) + start;
+ addChildWithName(placeholder, OFFSET, offset);
+ addChildWithName(placeholder, "useLength", "false");
+ }
+ }
+ }
+ }
+ XmlSerializer.deserializeInto(this, state);
}
public static CCProjectService getInstance(@NotNull Project project) {
import java.util.*;
public class CCUtils {
+ public static final String ANSWER_EXTENSION_DOTTED = ".answer.";
private static final Logger LOG = Logger.getInstance(CCUtils.class);
public static final String GENERATED_FILES_FOLDER = ".coursecreator";
public static final String COURSE_MODE = "Course Creator";
package com.jetbrains.edu.coursecreator;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.vfs.VirtualFile;
public class CCVirtualFileListener extends VirtualFileAdapter {
- private static final Logger LOG = Logger.getInstance(CCVirtualFileListener.class);
-
@Override
public void fileCreated(@NotNull VirtualFileEvent event) {
VirtualFile createdFile = event.getFile();
}
- private static boolean arePlaceholdersIntersect(@NotNull final TaskFile taskFile, @NotNull final Document document, int start, int end) {
+ private static boolean arePlaceholdersIntersect(@NotNull final TaskFile taskFile, int start, int end) {
List<AnswerPlaceholder> answerPlaceholders = taskFile.getAnswerPlaceholders();
for (AnswerPlaceholder existingAnswerPlaceholder : answerPlaceholders) {
- int twStart = existingAnswerPlaceholder.getRealStartOffset(document);
+ int twStart = existingAnswerPlaceholder.getOffset();
int twEnd = existingAnswerPlaceholder.getPossibleAnswerLength() + twStart;
if ((start >= twStart && start < twEnd) || (end > twStart && end <= twEnd) ||
(twStart >= start && twStart < end) || (twEnd > start && twEnd <= end)) {
}
final SelectionModel model = editor.getSelectionModel();
- final int start = model.getSelectionStart();
- final int lineNumber = document.getLineNumber(start);
- int realStart = start - document.getLineStartOffset(lineNumber);
+ final int offset = model.getSelectionStart();
final AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder();
- answerPlaceholder.setLine(lineNumber);
- answerPlaceholder.setStart(realStart);
+
+ answerPlaceholder.setOffset(offset);
answerPlaceholder.setUseLength(false);
+
String selectedText = model.getSelectedText();
answerPlaceholder.setPossibleAnswer(selectedText);
for (int i = 0; i < placeholders.size(); i++) {
AnswerPlaceholder fromPlaceholder = placeholders.get(i);
- taskFile.getAnswerPlaceholders().get(i).setInitialState(fromPlaceholder);
+ AnswerPlaceholder.MyInitialState state = fromPlaceholder.getInitialState();
+ taskFile.getAnswerPlaceholders().get(i).setInitialState(new AnswerPlaceholder.MyInitialState(state.getOffset(), state.getLength()));
}
}
}
int start = selectionModel.getSelectionStart();
int end = selectionModel.getSelectionEnd();
- return !arePlaceholdersIntersect(state.getTaskFile(), editor.getDocument(), start, end);
+ return !arePlaceholdersIntersect(state.getTaskFile(), start, end);
}
private static boolean canDeletePlaceholder(@NotNull CCState state) {
if (taskFile == null) {
return null;
}
- AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholder(editor.getDocument(),
- editor.getCaretModel().getLogicalPosition(),
- true);
+ AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholder(
+ editor.getCaretModel().getOffset()
+ );
return new CCState(taskFile, answerPlaceholder, psiFile, editor, project);
}
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.platform.templates.github.ZipUtil;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.core.EduDocumentListener;
import com.jetbrains.edu.learning.core.EduNames;
-import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.*;
-import com.jetbrains.edu.learning.oldCourseFormat.OldCourse;
import org.jetbrains.annotations.NotNull;
import java.io.*;
Reader reader = null;
try {
ZipUtil.unzip(null, new File(basePath), new File(virtualFile.getPath()), null, null, true);
- reader = new InputStreamReader(new FileInputStream(new File(basePath, EduNames.COURSE_META_FILE)));
- Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+ File courseMetaFile = new File(basePath, EduNames.COURSE_META_FILE);
+ reader = new InputStreamReader(new FileInputStream(courseMetaFile));
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Course.class, new StudySerializationUtils.Json.CourseTypeAdapter(courseMetaFile))
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .create();
Course course = gson.fromJson(reader, Course.class);
- if (course == null || course.getLessons().isEmpty() || StringUtil.isEmptyOrSpaces(course.getLessons().get(0).getName())) {
- try {
- reader.close();
- }
- catch (IOException e) {
- LOG.error(e.getMessage());
- }
- reader = new InputStreamReader(new FileInputStream(new File(basePath, EduNames.COURSE_META_FILE)));
- OldCourse oldCourse = gson.fromJson(reader, OldCourse.class);
- course = EduUtils.transformOldCourse(oldCourse);
+
+ if (course == null) {
+ Messages.showErrorDialog("This course is incompatible with current version", "Failed to Unpack Course");
+ return;
}
StudyTaskManager.getInstance(project).setCourse(course);
private static void replaceAnswerPlaceholder(@NotNull final Project project,
@NotNull final Document document,
@NotNull final AnswerPlaceholder answerPlaceholder) {
- final int offset = answerPlaceholder.getRealStartOffset(document);
+ final int offset = answerPlaceholder.getOffset();
CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
final String text = document.getText(TextRange.create(offset, offset + answerPlaceholder.getRealLength()));
answerPlaceholder.setTaskText(text);
return null;
}
Editor editor = FileEditorManager.getInstance(e.getProject()).getSelectedTextEditor();
- return editor == null ? null : taskFile.getAnswerPlaceholder(document,
- editor.offsetToLogicalPosition(offset));
+ return editor == null ? null : taskFile.getAnswerPlaceholder(offset);
}
public List<TextRange> select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) {
AnswerPlaceholder placeholder = getAnswerPlaceholder(e, cursorOffset);
assert placeholder != null;
- VirtualFile file = e.getContainingFile().getVirtualFile();
- Document document = FileDocumentManager.getInstance().getDocument(file);
- assert document != null;
- int startOffset = placeholder.getRealStartOffset(document);
+ int startOffset = placeholder.getOffset();
return Collections.singletonList(new TextRange(startOffset, startOffset + placeholder.getRealLength()));
}
}
--- /dev/null
+package com.jetbrains.edu.learning;
+
+import com.google.gson.*;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.hash.HashMap;
+import com.jetbrains.edu.learning.core.EduNames;
+import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseFormat.StudyStatus;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import org.jdom.Attribute;
+import org.jdom.Element;
+import org.jdom.output.XMLOutputter;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class StudySerializationUtils {
+
+ public static final String PLACEHOLDERS = "placeholders";
+ public static final String LINE = "line";
+ public static final String START = "start";
+ public static final String OFFSET = "offset";
+ public static final String TEXT = "text";
+ public static final String LESSONS = "lessons";
+ public static final String COURSE = "course";
+ public static final String COURSE_TITLED = "Course";
+ public static final String STATUS = "status";
+ public static final String AUTHOR = "author";
+ public static final String AUTHORS = "authors";
+ public static final String MY_INITIAL_START = "myInitialStart";
+
+ private StudySerializationUtils() {
+ }
+
+ public static class Xml {
+ public final static String COURSE_ELEMENT = "courseElement";
+ public final static String MAIN_ELEMENT = "StudyTaskManager";
+ public static final String MAP = "map";
+ public static final String KEY = "key";
+ public static final String VALUE = "value";
+ public static final String NAME = "name";
+ public static final String LIST = "list";
+ public static final String OPTION = "option";
+ public static final String INDEX = "index";
+ public static final String STUDY_STATUS_MAP = "myStudyStatusMap";
+ public static final String TASK_STATUS_MAP = "myTaskStatusMap";
+ public static final String LENGTH = "length";
+ public static final String ANSWER_PLACEHOLDERS = "answerPlaceholders";
+ public static final String TASK_LIST = "taskList";
+ public static final String TASK_FILES = "taskFiles";
+ public static final String INITIAL_STATE = "initialState";
+ public static final String MY_INITIAL_STATE = "MyInitialState";
+ public static final String MY_LINE = "myLine";
+ public static final String MY_START = "myStart";
+ public static final String MY_LENGTH = "myLength";
+ public static final String AUTHOR_TITLED = "Author";
+ public static final String FIRST_NAME = "first_name";
+ public static final String SECOND_NAME = "second_name";
+ public static final String MY_INITIAL_LINE = "myInitialLine";
+ public static final String MY_INITIAL_LENGTH = "myInitialLength";
+ public static final String ANSWER_PLACEHOLDER = "AnswerPlaceholder";
+ public static final String TASK_WINDOWS = "taskWindows";
+ public static final String RESOURCE_PATH = "resourcePath";
+ public static final String COURSE_DIRECTORY = "courseDirectory";
+
+ private Xml() {
+ }
+
+ public static int getVersion(Element element) {
+ if (element.getChild(COURSE_ELEMENT) != null) {
+ return 1;
+ }
+
+ final Element taskManager = element.getChild(MAIN_ELEMENT);
+
+ Element versionElement = getChildWithName(taskManager, "VERSION");
+ if (versionElement == null) {
+ return -1;
+ }
+
+ return Integer.valueOf(versionElement.getAttributeValue("value"));
+ }
+
+ public static Element convertToSecondVersion(Element element) {
+ final Element oldCourseElement = element.getChild(COURSE_ELEMENT);
+ Element state = new Element(MAIN_ELEMENT);
+
+ Element course = addChildWithName(state, COURSE, oldCourseElement.clone());
+ course.setName(COURSE_TITLED);
+
+ Element author = getChildWithName(course, AUTHOR);
+ String authorString = author.getAttributeValue(VALUE);
+ course.removeContent(author);
+
+ String[] names = authorString.split(" ", 2);
+ Element authorElement = new Element(AUTHOR_TITLED);
+ addChildWithName(authorElement, FIRST_NAME, names[0]);
+ addChildWithName(authorElement, SECOND_NAME, names.length == 1 ? "" : names[1]);
+
+ addChildList(course, AUTHORS, Arrays.asList(authorElement));
+
+ Element courseDirectoryElement = getChildWithName(course, RESOURCE_PATH);
+ renameElement(courseDirectoryElement, COURSE_DIRECTORY);
+
+ for (Element lesson : getChildList(course, LESSONS)) {
+ incrementIndex(lesson);
+ for (Element task : getChildList(lesson, TASK_LIST)) {
+ incrementIndex(task);
+ Map<String, Element> taskFiles = getChildMap(task, TASK_FILES);
+ for (Element taskFile : taskFiles.values()) {
+ renameElement(getChildWithName(taskFile, TASK_WINDOWS), ANSWER_PLACEHOLDERS);
+ for (Element placeholder : getChildList(taskFile, ANSWER_PLACEHOLDERS)) {
+ placeholder.setName(ANSWER_PLACEHOLDER);
+
+ Element initialState = new Element(MY_INITIAL_STATE);
+ addChildWithName(placeholder, INITIAL_STATE, initialState);
+ addChildWithName(initialState, MY_LINE, getChildWithName(placeholder, MY_INITIAL_LINE).getAttributeValue(VALUE));
+ addChildWithName(initialState, MY_START, getChildWithName(placeholder, MY_INITIAL_START).getAttributeValue(VALUE));
+ addChildWithName(initialState, MY_LENGTH, getChildWithName(placeholder, MY_INITIAL_LENGTH).getAttributeValue(VALUE));
+ }
+ }
+
+ }
+ }
+ element.removeContent();
+ element.addContent(state);
+ return element;
+ }
+
+ public static Map<String, String> fillStatusMap(Element taskManagerElement, String mapName, XMLOutputter outputter) {
+ Map<Element, String> sourceMap = getChildMap(taskManagerElement, mapName);
+ Map<String, String> destMap = new HashMap<>();
+ for (Map.Entry<Element, String> entry : sourceMap.entrySet()) {
+ String status = entry.getValue();
+ if (status.equals(StudyStatus.Unchecked.toString())) {
+ continue;
+ }
+ destMap.put(outputter.outputString(entry.getKey()), status);
+ }
+ return destMap;
+ }
+
+ public static Element convertToThirdVersion(Element state, Project project) {
+ Element taskManagerElement = state.getChild(MAIN_ELEMENT);
+ XMLOutputter outputter = new XMLOutputter();
+
+ Map<String, String> placeholderTextToStatus = fillStatusMap(taskManagerElement, STUDY_STATUS_MAP, outputter);
+ Map<String, String> taskFileToStatusMap = fillStatusMap(taskManagerElement, TASK_STATUS_MAP, outputter);
+
+ Element courseElement = getChildWithName(taskManagerElement, COURSE).getChild(COURSE_TITLED);
+ for (Element lesson : getChildList(courseElement, LESSONS)) {
+ int lessonIndex = getAsInt(lesson, INDEX);
+ for (Element task : getChildList(lesson, TASK_LIST)) {
+ String taskStatus = null;
+ int taskIndex = getAsInt(task, INDEX);
+ Map<String, Element> taskFiles = getChildMap(task, TASK_FILES);
+ for (Map.Entry<String, Element> entry : taskFiles.entrySet()) {
+ Element taskFileElement = entry.getValue();
+ String taskFileText = outputter.outputString(taskFileElement);
+ String taskFileStatus = taskFileToStatusMap.get(taskFileText);
+ if (taskFileStatus != null && (taskStatus == null || taskFileStatus.equals(StudyStatus.Failed.toString()))) {
+ taskStatus = taskFileStatus;
+ }
+ Document document = StudyUtils.getDocument(project.getBasePath(), lessonIndex, taskIndex, entry.getKey());
+ if (document == null) {
+ continue;
+ }
+ for (Element placeholder : getChildList(taskFileElement, ANSWER_PLACEHOLDERS)) {
+ taskStatus = addStatus(outputter, placeholderTextToStatus, taskStatus, placeholder);
+ addOffset(document, placeholder);
+ addInitialState(document, placeholder);
+ }
+ }
+ if (taskStatus != null) {
+ addChildWithName(task, STATUS, taskStatus);
+ }
+ }
+ }
+ return state;
+ }
+
+ public static String addStatus(XMLOutputter outputter,
+ Map<String, String> placeholderTextToStatus,
+ String taskStatus,
+ Element placeholder) {
+ String placeholderText = outputter.outputString(placeholder);
+ String status = placeholderTextToStatus.get(placeholderText);
+ if (status != null) {
+ addChildWithName(placeholder, STATUS, status);
+ if (taskStatus == null || status.equals(StudyStatus.Failed.toString())) {
+ taskStatus = status;
+ }
+ }
+ return taskStatus;
+ }
+
+ public static void addInitialState(Document document, Element placeholder) {
+ Element initialState = getChildWithName(placeholder, INITIAL_STATE).getChild(MY_INITIAL_STATE);
+ int initialLine = getAsInt(initialState, MY_LINE);
+ int initialStart = getAsInt(initialState, MY_START);
+ int initialOffset = document.getLineStartOffset(initialLine) + initialStart;
+ addChildWithName(initialState, OFFSET, initialOffset);
+ renameElement(getChildWithName(initialState, MY_LENGTH), LENGTH);
+ }
+
+ public static void addOffset(Document document, Element placeholder) {
+ int line = getAsInt(placeholder, LINE);
+ int start = getAsInt(placeholder, START);
+ int offset = document.getLineStartOffset(line) + start;
+ addChildWithName(placeholder, OFFSET, offset);
+ }
+
+ public static int getAsInt(Element element, String name) {
+ return Integer.valueOf(getChildWithName(element, name).getAttributeValue(VALUE));
+ }
+
+ public static void incrementIndex(Element element) {
+ Element index = getChildWithName(element, INDEX);
+ int indexValue = Integer.parseInt(index.getAttributeValue(VALUE));
+ changeValue(index, indexValue + 1);
+ }
+
+ public static void renameElement(Element element, String newName) {
+ element.setAttribute(NAME, newName);
+ }
+
+ public static void changeValue(Element element, Object newValue) {
+ element.setAttribute(VALUE, newValue.toString());
+ }
+
+ public static Element addChildWithName(Element parent, String name, Element value) {
+ Element child = new Element(OPTION);
+ child.setAttribute(NAME, name);
+ child.addContent(value);
+ parent.addContent(child);
+ return value;
+ }
+
+ public static Element addChildWithName(Element parent, String name, Object value) {
+ Element child = new Element(OPTION);
+ child.setAttribute(NAME, name);
+ child.setAttribute(VALUE, value.toString());
+ parent.addContent(child);
+ return child;
+ }
+
+ public static Element addChildList(Element parent, String name, List<Element> elements) {
+ Element listElement = new Element(LIST);
+ for (Element element : elements) {
+ listElement.addContent(element);
+ }
+ return addChildWithName(parent, name, listElement);
+ }
+
+ public static List<Element> getChildList(Element parent, String name) {
+ Element listParent = getChildWithName(parent, name);
+ if (listParent != null) {
+ Element list = listParent.getChild(LIST);
+ if (list != null) {
+ return list.getChildren();
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ @Nullable
+ public static Element getChildWithName(Element parent, String name) {
+ for (Element child : parent.getChildren()) {
+ Attribute attribute = child.getAttribute(NAME);
+ if (attribute == null) {
+ continue;
+ }
+ if (name.equals(attribute.getValue())) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ public static <K, V> Map<K, V> getChildMap(Element element, String name) {
+ Element mapParent = getChildWithName(element, name);
+ if (mapParent != null) {
+ Element map = mapParent.getChild(MAP);
+ if (map != null) {
+ HashMap result = new HashMap();
+ for (Element entry : map.getChildren()) {
+ Object key = entry.getAttribute(KEY) == null ? entry.getChild(KEY).getChildren().get(0) : entry.getAttributeValue(KEY);
+ Object value = entry.getAttribute(VALUE) == null ? entry.getChild(VALUE).getChildren().get(0) : entry.getAttributeValue(VALUE);
+ result.put(key, value);
+ }
+ return result;
+ }
+ }
+ return Collections.emptyMap();
+ }
+ }
+
+ public static class Json {
+
+ public static final String TASK_LIST = "task_list";
+ public static final String TASK_FILES = "task_files";
+
+ private Json() {
+ }
+
+ public static class CourseTypeAdapter implements JsonDeserializer<Course> {
+
+ private final File myCourseFile;
+
+ public CourseTypeAdapter(File courseFile) {
+ myCourseFile = courseFile;
+ }
+
+ @Override
+ public Course deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject courseObject = json.getAsJsonObject();
+ JsonArray lessons = courseObject.getAsJsonArray(LESSONS);
+ for (int lessonIndex = 1; lessonIndex <= lessons.size(); lessonIndex++) {
+ JsonObject lessonObject = lessons.get(lessonIndex - 1).getAsJsonObject();
+ JsonArray tasks = lessonObject.getAsJsonArray(TASK_LIST);
+ for (int taskIndex = 1; taskIndex <= tasks.size(); taskIndex++) {
+ JsonObject taskObject = tasks.get(taskIndex - 1).getAsJsonObject();
+ for (Map.Entry<String, JsonElement> taskFile : taskObject.getAsJsonObject(TASK_FILES).entrySet()) {
+ String name = taskFile.getKey();
+ String filePath = FileUtil.join(myCourseFile.getParent(), EduNames.LESSON + lessonIndex, EduNames.TASK + taskIndex, name);
+ VirtualFile resourceFile = LocalFileSystem.getInstance().findFileByIoFile(new File(filePath));
+ if (resourceFile == null) {
+ continue;
+ }
+ Document document = FileDocumentManager.getInstance().getDocument(resourceFile);
+ if (document == null) {
+ continue;
+ }
+ JsonObject taskFileObject = taskFile.getValue().getAsJsonObject();
+ JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS);
+ for (JsonElement placeholder : placeholders) {
+ JsonObject placeholderObject = placeholder.getAsJsonObject();
+ if (placeholderObject.getAsJsonPrimitive(OFFSET) != null) {
+ break;
+ }
+ int line = placeholderObject.getAsJsonPrimitive(LINE).getAsInt();
+ int start = placeholderObject.getAsJsonPrimitive(START).getAsInt();
+ int offset = document.getLineStartOffset(line) + start;
+ placeholderObject.addProperty(OFFSET, offset);
+ }
+ }
+ }
+ }
+ return new GsonBuilder().create().fromJson(json, Course.class);
+ }
+ }
+
+ public static class StepicTaskFileAdapter implements JsonDeserializer<TaskFile> {
+
+ @Override
+ public TaskFile deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject taskFileObject = json.getAsJsonObject();
+ JsonArray placeholders = taskFileObject.getAsJsonArray(PLACEHOLDERS);
+ for (JsonElement placeholder : placeholders) {
+ JsonObject placeholderObject = placeholder.getAsJsonObject();
+ int line = placeholderObject.getAsJsonPrimitive(LINE).getAsInt();
+ int start = placeholderObject.getAsJsonPrimitive(START).getAsInt();
+ if (line == -1) {
+ placeholderObject.addProperty(OFFSET, start);
+ } else {
+ Document document = EditorFactory.getInstance().createDocument(taskFileObject.getAsJsonPrimitive(TEXT).getAsString());
+ placeholderObject.addProperty(OFFSET, document.getLineStartOffset(line) + start);
+ }
+ }
+ return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create().fromJson(json, TaskFile.class);
+ }
+ }
+ }
+}
package com.jetbrains.edu.learning;
+import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
import com.intellij.ui.JBColor;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Transient;
-import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.*;
-import com.jetbrains.edu.learning.oldCourseFormat.OldCourse;
import com.jetbrains.edu.learning.stepic.StepicUser;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@State(name = "StudySettings", storages = @Storage("study_project.xml"))
public class StudyTaskManager implements PersistentStateComponent<Element>, DumbAware {
+ private static final Logger LOG = Logger.getInstance(StudyTaskManager.class);
+ public static final int CURRENT_VERSION = 3;
+ private StepicUser myUser;
private Course myCourse;
- private OldCourse myOldCourse;
- public int VERSION = 2;
- public StepicUser myUser;
- public Map<AnswerPlaceholder, StudyStatus> myStudyStatusMap = new HashMap<>();
- public Map<TaskFile, StudyStatus> myTaskStatusMap = new HashMap<>();
+ public int VERSION = 3;
+
public Map<Task, List<UserTest>> myUserTests = new HashMap<>();
public List<String> myInvisibleFiles = new ArrayList<>();
+
public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx();
private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT;
private boolean myTurnEditingMode = false;
- private StudyTaskManager() {
+ @Transient private final Project myProject;
+
+ public StudyTaskManager(Project project) {
+ myProject = project;
}
- public void setCourse(final Course course) {
+ public StudyTaskManager() {
+ this(null);
+ }
+
+ public void setCourse(Course course) {
myCourse = course;
}
}
}
-
- public void setStatus(Task task, StudyStatus status) {
- task.setStatus(status);
- for (TaskFile taskFile : task.getTaskFiles().values()) {
- setStatus(taskFile, status);
- }
- }
-
- public void setStatus(TaskFile file, StudyStatus status) {
- for (AnswerPlaceholder answerPlaceholder : file.getAnswerPlaceholders()) {
- setStatus(answerPlaceholder, status);
- }
- }
-
- public StudyStatus getStatus(AnswerPlaceholder placeholder) {
- StudyStatus status = placeholder.getStatus();
- if (status != StudyStatus.Uninitialized) return status;
-
- status = myStudyStatusMap.get(placeholder);
- if (status == null) {
- status = StudyStatus.Unchecked;
- }
- placeholder.setStatus(status);
- return status;
- }
-
-
- public StudyStatus getStatus(@NotNull final Lesson lesson) {
- for (Task task : lesson.getTaskList()) {
- StudyStatus taskStatus = getStatus(task);
- if (taskStatus == StudyStatus.Unchecked || taskStatus == StudyStatus.Failed) {
- return StudyStatus.Unchecked;
- }
- }
- return StudyStatus.Solved;
- }
-
- public StudyStatus getStatus(@NotNull final Task task) {
- StudyStatus taskStatus = task.getStatus();
- if (taskStatus != StudyStatus.Uninitialized) return taskStatus;
-
- for (TaskFile taskFile : task.getTaskFiles().values()) {
- StudyStatus taskFileStatus = getStatus(taskFile);
- if (taskFileStatus == StudyStatus.Unchecked) {
- task.setStatus(StudyStatus.Unchecked);
- removeObsoleteTaskStatus(task);
- return StudyStatus.Unchecked;
- }
- if (taskFileStatus == StudyStatus.Failed) {
- task.setStatus(StudyStatus.Failed);
- removeObsoleteTaskStatus(task);
- return StudyStatus.Failed;
- }
- }
- task.setStatus(StudyStatus.Solved);
- removeObsoleteTaskStatus(task);
- return StudyStatus.Solved;
- }
-
- private void removeObsoleteTaskStatus(Task task) {
- for (TaskFile taskFile: task.taskFiles.values()) {
- myTaskStatusMap.remove(taskFile);
-
- for (AnswerPlaceholder answerPlaceholder: taskFile.getAnswerPlaceholders()) {
- myStudyStatusMap.remove(answerPlaceholder);
- }
- }
-
- }
-
- private StudyStatus getStatus(@NotNull final TaskFile file) {
- if (file.getAnswerPlaceholders().isEmpty()) {
- if (myTaskStatusMap == null) return StudyStatus.Solved;
- return myTaskStatusMap.get(file);
-
- }
- for (AnswerPlaceholder answerPlaceholder : file.getAnswerPlaceholders()) {
- StudyStatus placeholderStatus = getStatus(answerPlaceholder);
- if (placeholderStatus == StudyStatus.Failed) {
- return StudyStatus.Failed;
- }
- if (placeholderStatus == StudyStatus.Unchecked) {
- return StudyStatus.Unchecked;
- }
- }
- return StudyStatus.Solved;
- }
-
-
public JBColor getColor(@NotNull final AnswerPlaceholder placeholder) {
- final StudyStatus status = getStatus(placeholder);
+ final StudyStatus status = placeholder.getStatus();
if (status == StudyStatus.Solved) {
return JBColor.GREEN;
}
}
public boolean hasFailedAnswerPlaceholders(@NotNull final TaskFile taskFile) {
- return taskFile.getAnswerPlaceholders().size() > 0 && getStatus(taskFile) == StudyStatus.Failed;
+ return taskFile.getAnswerPlaceholders().size() > 0 && taskFile.hasFailedPlaceholders();
}
@Nullable
public Element getState() {
Element el = new Element("taskManager");
if (myCourse != null) {
- Element courseElement = new Element(MAIN_ELEMENT);
+ Element courseElement = new Element(StudySerializationUtils.Xml.MAIN_ELEMENT);
XmlSerializer.serializeInto(this, courseElement);
el.addContent(courseElement);
}
@Override
public void loadState(Element state) {
- final Element mainElement = state.getChild(MAIN_ELEMENT);
- if (mainElement != null) {
- final StudyTaskManager taskManager = XmlSerializer.deserialize(mainElement, StudyTaskManager.class);
- if (taskManager != null) {
- myCourse = taskManager.myCourse;
- myUserTests = taskManager.myUserTests;
- myInvisibleFiles = taskManager.myInvisibleFiles;
- myTaskStatusMap = taskManager.myTaskStatusMap;
- myStudyStatusMap = taskManager.myStudyStatusMap;
- myShouldUseJavaFx = taskManager.myShouldUseJavaFx;
- myUser = taskManager.getUser();
- }
- }
- final Element oldCourseElement = state.getChild(COURSE_ELEMENT);
- if (oldCourseElement != null) {
- myOldCourse = XmlSerializer.deserialize(oldCourseElement, OldCourse.class);
- if (myOldCourse != null) {
- myCourse = EduUtils.transformOldCourse(myOldCourse, pair -> {
- setStatus(pair.first, pair.second);
- return null;
- });
- myOldCourse = null;
- }
- }
+ int version = StudySerializationUtils.Xml.getVersion(state);
+ if (version == -1) {
+ LOG.error("StudyTaskManager doesn't contain any version:\n" + state.getValue());
+ return;
+ }
+ switch (version) {
+ case 1:
+ state = StudySerializationUtils.Xml.convertToSecondVersion(state);
+ case 2:
+ state = StudySerializationUtils.Xml.convertToThirdVersion(state, myProject);
+ //uncomment for future versions
+ //case 3:
+ //state = StudySerializationUtils.Xml.convertToForthVersion(state, myProject);
+ }
+
+ XmlSerializer.deserializeInto(this, state.getChild(StudySerializationUtils.Xml.MAIN_ELEMENT));
+ VERSION = CURRENT_VERSION;
if (myCourse != null) {
myCourse.initCourse(true);
+ if (version != VERSION) {
+ String updatedCoursePath = FileUtil.join(PathManager.getConfigPath(), "courses", myCourse.getName());
+ if (new File(updatedCoursePath).exists()) {
+ myCourse.setCourseDirectory(updatedCoursePath);
+ }
+ }
}
}
- public static final String COURSE_ELEMENT = "courseElement";
- public static final String MAIN_ELEMENT = "StudyTaskManager";
-
- public OldCourse getOldCourse() {
- return myOldCourse;
- }
-
- public void setOldCourse(OldCourse oldCourse) {
- myOldCourse = oldCourse;
- }
-
public static StudyTaskManager getInstance(@NotNull final Project project) {
return ServiceManager.getService(project, StudyTaskManager.class);
}
myTurnEditingMode = turnEditingMode;
}
+ @Transient
public String getLogin() {
if (myUser != null) {
return myUser.getEmail();
}
return "";
}
-
+
+ @Transient
public void setLogin(String login) {
if (myUser != null) {
myUser.setEmail(login);
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
return new File(parent, EduNames.TASK_MD);
}
}
+
+ @Nullable
+ public static Document getDocument(String basePath, int lessonIndex, int taskIndex, String fileName) {
+ String taskPath = FileUtil.join(basePath, EduNames.LESSON + lessonIndex, EduNames.TASK + taskIndex);
+ VirtualFile taskFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.join(taskPath, fileName));
+ if (taskFile == null) {
+ taskFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.join(taskPath, EduNames.SRC, fileName));
+ }
+ if (taskFile == null) {
+ return null;
+ }
+ return FileDocumentManager.getInstance().getDocument(taskFile);
+ }
}
if (answer == null) {
continue;
}
- int offset = placeholder.getRealStartOffset(document);
+ int offset = placeholder.getOffset();
document.deleteString(offset, offset + placeholder.getRealLength());
document.insertString(offset, answer);
}
return;
}
AnswerPlaceholder.MyInitialState initialState = answerPlaceholder.getInitialState();
- int startOffset = patternDocument.getLineStartOffset(initialState.myLine) + initialState.myStart;
- final String text = patternDocument.getText(new TextRange(startOffset, startOffset + initialState.myLength));
+ int startOffset = initialState.getOffset();
+ final String text = patternDocument.getText(new TextRange(startOffset, startOffset + initialState.getLength()));
CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
Document document = studyState.getEditor().getDocument();
- int offset = answerPlaceholder.getRealStartOffset(document);
+ int offset = answerPlaceholder.getOffset();
document.deleteString(offset, offset + answerPlaceholder.getRealLength());
document.insertString(offset, text);
}), NAME, null);
}
final Editor editor = studyState.getEditor();
final TaskFile taskFile = studyState.getTaskFile();
- return taskFile.getAnswerPlaceholder(editor.getDocument(), editor.getCaretModel().getLogicalPosition());
+ return taskFile.getAnswerPlaceholder(editor.getCaretModel().getOffset());
}
}
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
-import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
-import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.StudyState;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
+import com.jetbrains.edu.learning.courseFormat.Course;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
}
PsiFile file = PsiManager.getInstance(project).findFile(studyState.getVirtualFile());
final Editor editor = studyState.getEditor();
- LogicalPosition pos = editor.getCaretModel().getLogicalPosition();
- AnswerPlaceholder answerPlaceholder = studyState.getTaskFile().getAnswerPlaceholder(editor.getDocument(), pos);
+ int offset = editor.getCaretModel().getOffset();
+ AnswerPlaceholder answerPlaceholder = studyState.getTaskFile().getAnswerPlaceholder(
+ offset);
if (file == null) {
return;
}
String hint = answerPlaceholder.getHint();
hintText = hint.isEmpty() ? HINT_NOT_AVAILABLE : hint;
}
- int offset = editor.getDocument().getLineStartOffset(pos.line) + pos.column;
PsiElement element = file.findElementAt(offset);
DocumentationManager documentationManager = DocumentationManager.getInstance(project);
DocumentationComponent component = new DocumentationComponent(documentationManager);
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.navigation.StudyNavigator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
if (nextAnswerPlaceholder == null) {
return;
}
- StudyNavigator.navigateToAnswerPlaceholder(selectedEditor, nextAnswerPlaceholder, selectedTaskFile);
+ StudyNavigator.navigateToAnswerPlaceholder(selectedEditor, nextAnswerPlaceholder);
selectedEditor.getSelectionModel().removeSelection();
}
}
@Nullable
private static AnswerPlaceholder getSelectedAnswerPlaceholder(@NotNull final Editor editor, @NotNull final TaskFile file) {
- LogicalPosition position = editor.getCaretModel().getLogicalPosition();
- return file.getAnswerPlaceholder(editor.getDocument(), position);
+ return file.getAnswerPlaceholder(editor.getCaretModel().getOffset());
}
@Nullable
myTask = studyState.getTask();
myTaskDir = studyState.getTaskDir();
myTaskManger = StudyTaskManager.getInstance(myProject);
- myStatusBeforeCheck = myTaskManger.getStatus(myTask);
+ myStatusBeforeCheck = myTask.getStatus();
}
@Override
@Override
public void onCancel() {
- myTaskManger.setStatus(myTask, myStatusBeforeCheck);
+ myTask.setStatus(myStatusBeforeCheck);
clearState();
}
}
protected void onTaskFailed(String message) {
- myTaskManger.setStatus(myTask, StudyStatus.Failed);
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
-
+ myTask.setStatus(StudyStatus.Failed);
if (course != null) {
if (course.isAdaptive()) {
ApplicationManager.getApplication().invokeLater(
}
protected void onTaskSolved(String message) {
- myTaskManger.setStatus(myTask, StudyStatus.Solved);
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
-
+ myTask.setStatus(StudyStatus.Solved);
if (course != null) {
if (course.isAdaptive()) {
ApplicationManager.getApplication().invokeLater(
if (!answerPlaceholder.isValid(document)) {
continue;
}
- final int start = answerPlaceholder.getRealStartOffset(document);
+ final int start = answerPlaceholder.getOffset();
final int end = start + answerPlaceholder.getRealLength();
final String text = answerPlaceholder.getPossibleAnswer();
document.replaceString(start, end, text);
TaskFile.copy(answerTaskFile, windowTaskFile);
EduDocumentListener listener = new EduDocumentListener(windowTaskFile);
windowDocument.addDocumentListener(listener);
- int start = placeholder.getRealStartOffset(windowDocument);
+ int start = placeholder.getOffset();
int end = start + placeholder.getRealLength();
final AnswerPlaceholder userAnswerPlaceholder = usersTaskFile.getAnswerPlaceholders().get(placeholder.getIndex());
- int userStart = userAnswerPlaceholder.getRealStartOffset(usersDocument);
+ int userStart = userAnswerPlaceholder.getOffset();
int userEnd = userStart + userAnswerPlaceholder.getRealLength();
String text = usersDocument.getText(new TextRange(userStart, userEnd));
windowDocument.replaceString(start, end, text);
EffectType.BOXED, Font.PLAIN);
final Project project = editor.getProject();
assert project != null;
- final int startOffset = placeholder.getRealStartOffset(document);
+ final int startOffset = placeholder.getOffset();
+ if (startOffset == - 1) {
+ return;
+ }
final int length = placeholder.getRealLength();
final int endOffset = startOffset + length;
textAttributes.setEffectColor(color);
DocumentImpl documentImpl = (DocumentImpl)document;
List<RangeMarker> blocks = documentImpl.getGuardedBlocks();
if (!placeholder.isValid(document)) return;
- int start = placeholder.getRealStartOffset(document);
+ int start = placeholder.getOffset();
final int length = placeholder.getRealLength();
int end = start + length;
if (start != 0) {
return;
}
myTaskFile.setHighlightErrors(true);
- Document document = e.getDocument();
myAnswerPlaceholders.clear();
for (AnswerPlaceholder answerPlaceholder : myTaskFile.getAnswerPlaceholders()) {
- int twStart = answerPlaceholder.getRealStartOffset(document);
+ int twStart = answerPlaceholder.getOffset();
int length = answerPlaceholder.getRealLength();
int twEnd = twStart + length;
myAnswerPlaceholders.add(new AnswerPlaceholderWrapper(answerPlaceholder, twStart, twEnd));
twEnd += change;
}
AnswerPlaceholder answerPlaceholder = answerPlaceholderWrapper.getAnswerPlaceholder();
- int line = document.getLineNumber(twStart);
- int start = twStart - document.getLineStartOffset(line);
int length = twEnd - twStart;
- answerPlaceholder.setLine(line);
- answerPlaceholder.setStart(start);
+ answerPlaceholder.setOffset(twStart);
+ answerPlaceholder.setLength(length);
if (!answerPlaceholder.getUseLength()) {
answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(twStart, twStart + length)));
}
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiDirectory;
-import com.intellij.util.Function;
-import com.intellij.util.containers.HashMap;
import com.jetbrains.edu.learning.courseFormat.*;
-import com.jetbrains.edu.learning.oldCourseFormat.OldCourse;
-import com.jetbrains.edu.learning.oldCourseFormat.TaskWindow;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.imageio.ImageIO;
-import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
printWriter.println("#educational_plugin_window = ");
continue;
}
- int start = answerPlaceholder.getRealStartOffset(document);
+ int start = answerPlaceholder.getOffset();
final String windowDescription = document.getText(new TextRange(start, start + length));
printWriter.println("#educational_plugin_window = " + windowDescription);
}
taskFile.sortAnswerPlaceholders();
for (int i = taskFile.getAnswerPlaceholders().size() - 1; i >= 0; i--) {
final AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholders().get(i);
- if (answerPlaceholder.getRealStartOffset(document) > document.getTextLength() || answerPlaceholder.getRealStartOffset(document) + answerPlaceholder.getPossibleAnswerLength() > document.getTextLength()) {
- LOG.error("Wrong startOffset: " + answerPlaceholder.getRealStartOffset(document) + "; document: " + file.getPath());
+ int offset = answerPlaceholder.getOffset();
+ if (offset > document.getTextLength() || offset + answerPlaceholder.getPossibleAnswerLength() > document.getTextLength()) {
+ LOG.error("Wrong startOffset: " + answerPlaceholder.getOffset() + "; document: " + file.getPath());
return;
}
replaceAnswerPlaceholder(project, document, answerPlaceholder);
@NotNull final Document document,
@NotNull final AnswerPlaceholder answerPlaceholder) {
final String taskText = answerPlaceholder.getTaskText();
- final int offset = answerPlaceholder.getRealStartOffset(document);
+ final int offset = answerPlaceholder.getOffset();
CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
document.replaceString(offset, offset + answerPlaceholder.getPossibleAnswerLength(), taskText);
FileDocumentManager.getInstance().saveDocument(document);
return lesson.getTask(directory.getName());
}
- @NotNull
- public static Course transformOldCourse(@NotNull final OldCourse oldCourse) {
- return transformOldCourse(oldCourse, null);
- }
-
- @NotNull
- public static Course transformOldCourse(@NotNull final OldCourse oldCourse,
- @Nullable Function<Pair<AnswerPlaceholder, StudyStatus>, Void> setStatus) {
- Course course = new Course();
- course.setDescription(oldCourse.description);
- course.setName(oldCourse.name);
- course.setAuthors(new String[]{oldCourse.author});
-
- String updatedCoursePath = FileUtil.join(PathManager.getConfigPath(), "courses", oldCourse.name);
- if (new File(updatedCoursePath).exists()) {
- course.setCourseDirectory(FileUtil.toSystemIndependentName(updatedCoursePath));
- }
- final ArrayList<Lesson> lessons = new ArrayList<Lesson>();
- for (com.jetbrains.edu.learning.oldCourseFormat.Lesson oldLesson : oldCourse.lessons) {
- final Lesson lesson = new Lesson();
- lesson.setName(oldLesson.name);
- lesson.setIndex(oldLesson.myIndex + 1);
-
- final ArrayList<Task> tasks = new ArrayList<Task>();
- for (com.jetbrains.edu.learning.oldCourseFormat.Task oldTask : oldLesson.taskList) {
- final Task task = new Task();
- task.setIndex(oldTask.myIndex + 1);
- task.setName(oldTask.name);
- task.setLesson(lesson);
- final HashMap<String, TaskFile> taskFiles = new HashMap<String, TaskFile>();
- for (Map.Entry<String, com.jetbrains.edu.learning.oldCourseFormat.TaskFile> entry : oldTask.taskFiles.entrySet()) {
- final TaskFile taskFile = new TaskFile();
- final com.jetbrains.edu.learning.oldCourseFormat.TaskFile oldTaskFile = entry.getValue();
- taskFile.setIndex(oldTaskFile.myIndex);
- taskFile.name = entry.getKey();
-
- final ArrayList<AnswerPlaceholder> placeholders = new ArrayList<AnswerPlaceholder>();
- for (TaskWindow window : oldTaskFile.taskWindows) {
- final AnswerPlaceholder placeholder = new AnswerPlaceholder();
- placeholder.setIndex(window.myIndex);
- placeholder.setHint(window.hint);
- placeholder.setLength(window.length);
- placeholder.setLine(window.line);
- placeholder.setPossibleAnswer(window.possibleAnswer);
- placeholder.setStart(window.start);
- placeholders.add(placeholder);
- placeholder.setInitialState(new AnswerPlaceholder.MyInitialState(window.myInitialLine,
- window.myInitialLength,
- window.myInitialStart));
- if (setStatus != null) {
- setStatus.fun(Pair.create(placeholder, window.myStatus));
- }
- }
-
- taskFile.setAnswerPlaceholders(placeholders);
- taskFiles.put(entry.getKey(), taskFile);
- }
- task.taskFiles = taskFiles;
- tasks.add(task);
- }
-
- lesson.taskList = tasks;
-
- lessons.add(lesson);
- }
- course.setLessons(lessons);
- course.initCourse(true);
- return course;
- }
-
public static boolean isImage(String fileName) {
final String[] readerFormatNames = ImageIO.getReaderFormatNames();
for (@NonNls String format : readerFormatNames) {
public class AnswerPlaceholder {
- @Expose private int line = 0;
- @Expose private int start = 0;
@Expose private String hint = "";
@SerializedName("possible_answer")
@Expose private String possibleAnswer = "";
- @Expose private int length = 0;
+
+ @SerializedName("offset")
+ @Expose
+ private int myOffset = -1;
+
+ @Expose private int length = -1;
+
private int myIndex = -1;
private String myTaskText;
private MyInitialState myInitialState;
- private StudyStatus myStatus = StudyStatus.Uninitialized;
+ private StudyStatus myStatus = StudyStatus.Unchecked;
private boolean mySelected = false;
private boolean myUseLength = true;
- @Transient private TaskFile myTaskFile;
+
+ @Transient
+ private TaskFile myTaskFile;
+
+ public AnswerPlaceholder() {
+ }
public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) {
if (!isRestarted) {
- setInitialState(new MyInitialState(getLine(), getLength(), getStart()));
+ setInitialState(new MyInitialState(myOffset, length));
myStatus = file.getTask().getStatus();
}
this.length = length;
}
- public int getStart() {
- return start;
- }
-
- public void setStart(int start) {
- this.start = start;
- }
-
- public int getLine() {
- return line;
- }
-
- public void setLine(int line) {
- this.line = line;
- }
-
public String getHint() {
return hint;
}
return myInitialState;
}
- public void setInitialState(@NotNull final MyInitialState initialState) {
+ public void setInitialState(MyInitialState initialState) {
myInitialState = initialState;
}
myTaskFile = taskFile;
}
- public int getRealStartOffset(@NotNull final Document document) {
- return document.getLineStartOffset(line) + start;
- }
-
public int getPossibleAnswerLength() {
return possibleAnswer.length();
}
}
public boolean isValid(@NotNull final Document document, int length) {
- boolean isLineValid = line < document.getLineCount() && line >= 0;
- if (!isLineValid) return false;
- boolean isStartValid = start >= 0 && start < document.getLineEndOffset(line);
- boolean isLengthValid = (getRealStartOffset(document) + length) <= document.getTextLength();
- return isLengthValid && isStartValid;
+ return myOffset >= 0 && myOffset + length <= document.getTextLength();
}
/**
* Returns window to its initial state
*/
public void reset() {
- line = myInitialState.myLine;
- start = myInitialState.myStart;
- length = myInitialState.myLength;
- if (!getUseLength()) {
+ myOffset = myInitialState.getOffset();
+ length = myInitialState.getLength();
+ if (!myUseLength) {
possibleAnswer = myTaskText;
}
}
}
public void init() {
- setInitialState(new MyInitialState(line, myTaskText.length(), start));
+ setInitialState(new MyInitialState(myOffset, myTaskText.length()));
}
public boolean getUseLength() {
myUseLength = useLength;
}
- public void setInitialState(AnswerPlaceholder placeholder) {
- setInitialState(new MyInitialState(placeholder.line, placeholder.length, placeholder.start));
+ public int getOffset() {
+ return myOffset;
+ }
+
+ public void setOffset(int offset) {
+ myOffset = offset;
}
public static class MyInitialState {
- public int myLine = -1;
- public int myLength = -1;
- public int myStart = -1;
+ private int length = -1;
+ private int offset = -1;
public MyInitialState() {
}
- public MyInitialState(int line, int length, int start) {
- myLine = line;
- myLength = length;
- myStart = start;
+ public MyInitialState(int initialOffset, int length) {
+ this.offset = initialOffset;
+ this.length = length;
}
- }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- AnswerPlaceholder that = (AnswerPlaceholder)o;
+ public int getLength() {
+ return length;
+ }
- if (getLine() != that.getLine()) return false;
- if (getStart() != that.getStart()) return false;
- if (getLength() != that.getLength()) return false;
- if (getIndex() != that.getIndex()) return false;
- if (getHint() != null ? !getHint().equals(that.getHint()) : that.getHint() != null) return false;
- if (getPossibleAnswer() != null ? !getPossibleAnswer().equals(that.getPossibleAnswer()) : that.getPossibleAnswer() != null)
- return false;
- if (myTaskText != null ? !myTaskText.equals(that.myTaskText) : that.myTaskText != null) return false;
+ public void setLength(int length) {
+ this.length = length;
+ }
- return true;
- }
+ public int getOffset() {
+ return offset;
+ }
- @Override
- public int hashCode() {
- int result = getLine();
- result = 31 * result + getStart();
- result = 31 * result + (getHint() != null ? getHint().hashCode() : 0);
- result = 31 * result + (getPossibleAnswer() != null ? getPossibleAnswer().hashCode() : 0);
- result = 31 * result + getLength();
- result = 31 * result + getIndex();
- result = 31 * result + (myTaskText != null ? myTaskText.hashCode() : 0);
- return result;
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
}
}
public class AnswerPlaceholderComparator implements Comparator<AnswerPlaceholder> {
@Override
public int compare(AnswerPlaceholder o1, AnswerPlaceholder answerPlaceholder) {
- final int line = o1.getLine();
- int lineDiff = line - answerPlaceholder.getLine();
- if (lineDiff == 0) {
- return o1.getStart() - answerPlaceholder.getStart();
- }
- return lineDiff;
+ return o1.getOffset() - answerPlaceholder.getOffset();
}
@Override
import com.google.gson.annotations.SerializedName;
import com.intellij.lang.Language;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.Function;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.stepic.StepicUser;
@Expose private String courseType = EduNames.PYCHARM;
@Expose private String courseMode = EduNames.STUDY; //this field is used to distinguish study and course creator modes
+ public Course() {
+ }
+
/**
* Initializes state of course
*/
public class Lesson implements StudyItem {
@Expose public int id;
- @Expose private int myIndex = -1; // index is visible to user number of lesson from 1 to lesson number
- @Expose @Transient public List<Integer> steps;
- @Expose @SerializedName("title") private String name;
- @Expose @SerializedName("task_list") public List<Task> taskList = new ArrayList<Task>();
+ @Transient public List<Integer> steps;
@Transient public List<String> tags;
- @Transient private Course myCourse = null;
+ @Transient
+ Boolean is_public;
+
+ @Expose
+ @SerializedName("title")
+ private String name;
+
+ @Expose
+ @SerializedName("task_list")
+ public List<Task> taskList = new ArrayList<Task>();
+
+ @Transient
+ private Course myCourse = null;
+
+ // index is visible to user number of lesson from 1 to lesson number
+ @Expose private int myIndex = -1;
+
+ public Lesson() {
+ }
public void initLesson(final Course course, boolean isRestarted) {
setCourse(course);
return null;
}
+ public StudyStatus getStatus() {
+ for (Task task : taskList) {
+ if (task.getStatus() != StudyStatus.Solved) {
+ return StudyStatus.Unchecked;
+ }
+ }
+ return StudyStatus.Solved;
+ }
}
package com.jetbrains.edu.learning.courseFormat;
public enum StudyStatus {
- Unchecked, Solved, Failed, Uninitialized
+ Unchecked, Solved, Failed
}
* Implementation of task which contains task files, tests, input file for tests
*/
public class Task implements StudyItem {
- @Expose private String name;
- @Expose private String text;
+ @Expose
+ private String name;
+
+ // index is visible to user number of task from 1 to task number
+ @Expose private int myIndex;
+ @Expose private StudyStatus myStatus = StudyStatus.Unchecked;
+
@Expose private int myStepicId;
- @Expose private int myIndex; // index is visible to user number of task from 1 to task number
- @Expose @SerializedName("task_files") public Map<String, TaskFile> taskFiles = new HashMap<String, TaskFile>();
- @Expose private Map<String, String> testsText = new HashMap<String, String>();
- @Expose private StudyStatus myStatus = StudyStatus.Uninitialized;
+
+ @Expose
+ @SerializedName("task_files")
+ public Map<String, TaskFile> taskFiles = new HashMap<String, TaskFile>();
+
+ @Expose private String text;
+ private Map<String, String> testsText = new HashMap<String, String>();
+
@Transient private Lesson myLesson;
public Task() {}
public void setStatus(StudyStatus status) {
myStatus = status;
+ for (TaskFile taskFile : taskFiles.values()) {
+ for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
+ placeholder.setStatus(status);
+ }
+ }
}
}
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.util.xmlb.annotations.Transient;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Expose @SerializedName("placeholders") private List<AnswerPlaceholder> myAnswerPlaceholders = new ArrayList<AnswerPlaceholder>();
@Transient private Task myTask;
+ public TaskFile() {
+ }
+
public void initTaskFile(final Task task, boolean isRestarted) {
setTask(task);
final List<AnswerPlaceholder> answerPlaceholders = getAnswerPlaceholders();
}
/**
- * @param pos position in editor
- * @return task window located in specified position or null if there is no task window in this position
+ * @param offset position in editor
+ * @return answer placeholder located in specified position or null if there is no task window in this position
*/
@Nullable
- public AnswerPlaceholder getAnswerPlaceholder(@NotNull final Document document, @NotNull final LogicalPosition pos) {
- return getAnswerPlaceholder(document, pos, false);
- }
-
- @Nullable
- public AnswerPlaceholder getAnswerPlaceholder(@NotNull final Document document, @NotNull final LogicalPosition pos,
- boolean useAnswerLength) {
- int line = pos.line;
- if (line >= document.getLineCount()) {
- return null;
- }
- int column = pos.column;
- int offset = document.getLineStartOffset(line) + column;
+ public AnswerPlaceholder getAnswerPlaceholder(int offset) {
for (AnswerPlaceholder placeholder : myAnswerPlaceholders) {
- if (placeholder.getLine() <= line) {
- int realStartOffset = placeholder.getRealStartOffset(document);
- int placeholderLength = placeholder.getRealLength();
- final int length = placeholderLength > 0 ? placeholderLength : 0;
- int endOffset = realStartOffset + length;
- if (realStartOffset <= offset && offset <= endOffset) {
- return placeholder;
- }
+ int placeholderStart = placeholder.getOffset();
+ int placeholderEnd = placeholderStart + placeholder.getRealLength();
+ if (placeholderStart <= offset && offset <= placeholderEnd) {
+ return placeholder;
}
}
return null;
List<AnswerPlaceholder> answerPlaceholdersCopy = new ArrayList<AnswerPlaceholder>(sourceAnswerPlaceholders.size());
for (AnswerPlaceholder answerPlaceholder : sourceAnswerPlaceholders) {
AnswerPlaceholder answerPlaceholderCopy = new AnswerPlaceholder();
- answerPlaceholderCopy.setLine(answerPlaceholder.getLine());
- answerPlaceholderCopy.setStart(answerPlaceholder.getStart());
answerPlaceholderCopy.setTaskText(answerPlaceholder.getTaskText());
+ answerPlaceholderCopy.setOffset(answerPlaceholder.getOffset());
answerPlaceholderCopy.setLength(answerPlaceholder.getLength());
answerPlaceholderCopy.setPossibleAnswer(answerPlaceholder.getPossibleAnswer());
answerPlaceholderCopy.setIndex(answerPlaceholder.getIndex());
}
}
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- TaskFile that = (TaskFile)o;
-
- if (getIndex() != that.getIndex()) return false;
- if (!name.equals(that.name)) return false;
-
- final List<AnswerPlaceholder> answerPlaceholders = getAnswerPlaceholders();
- final List<AnswerPlaceholder> thatAnswerPlaceholders = that.getAnswerPlaceholders();
- if (answerPlaceholders.size() != thatAnswerPlaceholders.size()) return false;
- for (int i = 0; i < answerPlaceholders.size(); i++) {
- final AnswerPlaceholder placeholder = answerPlaceholders.get(i);
- final AnswerPlaceholder thatPlaceholder = thatAnswerPlaceholders.get(i);
- if (!placeholder.equals(thatPlaceholder)) return false;
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = getIndex();
- result = 31 * result + name.hashCode();
+ public boolean hasFailedPlaceholders() {
for (AnswerPlaceholder placeholder : myAnswerPlaceholders) {
- result = 31 * result + placeholder.hashCode();
+ if (placeholder.getStatus() == StudyStatus.Failed) {
+ return true;
+ }
}
- return result;
+ return false;
}
}
import com.intellij.psi.PsiManager;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.edu.learning.StudyProjectComponent;
+import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
Reader reader = null;
try {
reader = new InputStreamReader(new FileInputStream(courseFile), "UTF-8");
- Gson gson = new GsonBuilder().create();
+ Gson gson = new GsonBuilder().registerTypeAdapter(Course.class, new StudySerializationUtils.Json.CourseTypeAdapter(courseFile)).create();
final Course course = gson.fromJson(reader, Course.class);
course.initCourse(isAdaptive);
return course;
final Editor editor = e.getEditor();
final Point point = e.getMouseEvent().getPoint();
final LogicalPosition pos = editor.xyToLogicalPosition(point);
- final AnswerPlaceholder answerPlaceholder = myTaskFile.getAnswerPlaceholder(editor.getDocument(), pos);
+ final AnswerPlaceholder answerPlaceholder = myTaskFile.getAnswerPlaceholder(editor.logicalPositionToOffset(pos));
if (answerPlaceholder == null || answerPlaceholder.getSelected()) {
return;
}
- int startOffset = answerPlaceholder.getRealStartOffset(editor.getDocument());
+ int startOffset = answerPlaceholder.getOffset();
editor.getSelectionModel().setSelection(startOffset, startOffset + answerPlaceholder.getRealLength());
answerPlaceholder.setSelected(true);
}
package com.jetbrains.edu.learning.navigation;
import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.project.Project;
-import com.jetbrains.edu.learning.core.EduNames;
-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.*;
import org.jetbrains.annotations.NotNull;
final Project project = editor.getProject();
if (project == null) return;
for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) {
- final StudyStatus status = StudyTaskManager.getInstance(project).getStatus(answerPlaceholder);
- if (status != StudyStatus.Failed) {
+ if (answerPlaceholder.getStatus() != StudyStatus.Failed) {
continue;
}
- navigateToAnswerPlaceholder(editor, answerPlaceholder, taskFile);
+ navigateToAnswerPlaceholder(editor, answerPlaceholder);
break;
}
}
- public static void navigateToAnswerPlaceholder(@NotNull final Editor editor, @NotNull final AnswerPlaceholder answerPlaceholder,
- @NotNull final TaskFile taskFile) {
+ public static void navigateToAnswerPlaceholder(@NotNull final Editor editor, @NotNull final AnswerPlaceholder answerPlaceholder) {
if (editor.isDisposed() || !answerPlaceholder.isValid(editor.getDocument())) {
return;
}
- LogicalPosition placeholderStart = new LogicalPosition(answerPlaceholder.getLine(), answerPlaceholder.getStart());
- editor.getCaretModel().moveToLogicalPosition(placeholderStart);
+ editor.getCaretModel().moveToOffset(answerPlaceholder.getOffset());
}
public static void navigateToFirstAnswerPlaceholder(@NotNull final Editor editor, @NotNull final TaskFile taskFile) {
if (!taskFile.getAnswerPlaceholders().isEmpty()) {
AnswerPlaceholder firstAnswerPlaceholder = StudyUtils.getFirst(taskFile.getAnswerPlaceholders());
- navigateToAnswerPlaceholder(editor, firstAnswerPlaceholder, taskFile);
+ navigateToAnswerPlaceholder(editor, firstAnswerPlaceholder);
}
}
+++ /dev/null
-package com.jetbrains.edu.learning.oldCourseFormat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class Lesson {
- public String name;
- public List<Task> taskList = new ArrayList<Task>();
- public int myIndex = -1;
- public LessonInfo myLessonInfo = new LessonInfo();
-
-}
+++ /dev/null
-package com.jetbrains.edu.learning.oldCourseFormat;
-
-/**
- * Implementation of class which contains information about student progress in current lesson
- */
-public class LessonInfo {
- public int myTaskNum;
- public int myTaskFailed;
- public int myTaskSolved;
- public int myTaskUnchecked;
-
-}
+++ /dev/null
-package com.jetbrains.edu.learning.oldCourseFormat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class OldCourse {
-
- public List<Lesson> lessons = new ArrayList<Lesson>();
- public String description;
- public String name;
- public String myResourcePath = "";
- public String author;
- public boolean myUpToDate = false;
-
-}
\ No newline at end of file
+++ /dev/null
-package com.jetbrains.edu.learning.oldCourseFormat;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Implementation of task which contains task files, tests, input file for tests
- */
-public class Task {
- public String name;
- public Map<String, TaskFile> taskFiles = new HashMap<String, TaskFile>();
- public int myIndex;
-}
+++ /dev/null
-package com.jetbrains.edu.learning.oldCourseFormat;
-
-import com.intellij.util.xmlb.annotations.Transient;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class TaskFile {
- public List<TaskWindow> taskWindows = new ArrayList<TaskWindow>();
- @Transient
- public int myIndex = -1;
-}
+++ /dev/null
-package com.jetbrains.edu.learning.oldCourseFormat;
-
-import com.jetbrains.edu.learning.courseFormat.StudyStatus;
-
-/**
- * Implementation of windows which user should type in
- */
-
-
-public class TaskWindow {
- public int line = 0;
- public int start = 0;
- public String hint = "";
- public String possibleAnswer = "";
- public int length = 0;
- public int myIndex = -1;
- public int myInitialLine = -1;
- public int myInitialStart = -1;
- public int myInitialLength = -1;
- public StudyStatus myStatus = StudyStatus.Unchecked;
-}
\ No newline at end of file
if (course == null) {
return;
}
- if (valueName.equals(myProject.getName())) {
+ if (valueName.equals(myProject.getBaseDir().getName())) {
data.clearText();
data.setIcon(InteractiveLearningIcons.Course);
data.addText(course.getName(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.BLACK));
return name.contains(EduNames.SANDBOX_DIR) ? 0 : 3;
}
- private void setStudyAttributes(Lesson lesson, PresentationData data, String additionalName) {
- StudyStatus taskStatus = StudyTaskManager.getInstance(myProject).getStatus(lesson);
- switch (taskStatus) {
+ private static void setStudyAttributes(Lesson lesson, PresentationData data, String additionalName) {
+ switch (lesson.getStatus()) {
case Unchecked: {
updatePresentation(data, additionalName, JBColor.BLACK, InteractiveLearningIcons.Lesson);
break;
}
protected void setStudyAttributes(Task task, PresentationData data, String additionalName) {
- StudyStatus taskStatus = StudyTaskManager.getInstance(myProject).getStatus(task);
+ StudyStatus taskStatus = task.getStatus();
switch (taskStatus) {
case Unchecked: {
updatePresentation(data, additionalName, JBColor.BLACK, InteractiveLearningIcons.Task);
public void setAuthors(List<StepicUser> authors) {
myAuthors = authors;
for (StepicUser author : authors) {
- if (author.id > 0) {
- instructors.add(author.id);
+ if (author.getId() > 0) {
+ instructors.add(author.getId());
}
}
}
final boolean recommendationReaction =
user != null && postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().id),
- String.valueOf(user.id), reaction);
+ String.valueOf(user.getId()), reaction);
if (recommendationReaction) {
final Task task = getNextRecommendation(project, course);
package com.jetbrains.edu.learning.stepic;
-import com.google.gson.FieldNamingPolicy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import com.google.gson.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileFilter;
import com.intellij.util.net.ssl.CertificateManager;
+import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
throw new IOException("Stepic returned non 200 status code " + responseString);
}
- Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+ Gson gson = new GsonBuilder().registerTypeAdapter(TaskFile.class, new StudySerializationUtils.Json.StepicTaskFileAdapter()).setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
return gson.fromJson(responseString, container);
}
public static void postTask(final Project project, @NotNull final Task task, final int lessonId) {
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES);
setHeaders(request, "application/json");
+ //TODO: register type adapter for task files here?
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
ApplicationManager.getApplication().invokeLater(() -> {
final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
public class StepicUser {
private static final String STEPIC_SETTINGS_PASSWORD_KEY = "STEPIC_SETTINGS_PASSWORD_KEY";
private static final Logger LOG = Logger.getInstance(StepicUser.class);
- int id;
- String firstName;
- String lastName;
- String email;
+ private int id = -1;
+ private String myFirstName = "";
+ private String myLastName = "";
+ private String myEmail = "";
public StepicUser() {
}
public StepicUser(String email, String password) {
- this.email = email;
+ this.myEmail = email;
setPassword(password);
}
}
public String getFirstName() {
- return firstName;
+ return myFirstName;
}
public void setFirstName(String firstName) {
- this.firstName = firstName;
+ this.myFirstName = firstName;
}
public String getLastName() {
- return lastName;
+ return myLastName;
}
public void setLastName(String last_name) {
- this.lastName = last_name;
+ this.myLastName = last_name;
}
public String getEmail() {
- return email;
+ return myEmail;
}
public void setEmail(String email) {
- this.email = email;
+ this.myEmail = email;
}
@Transient
}
public String getName() {
- return StringUtil.join(new String[]{firstName, lastName}, " ");
+ return StringUtil.join(new String[]{myFirstName, myLastName}, " ");
}
}
for (Lesson lesson : lessons) {
if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) continue;
taskNum += lesson.getTaskList().size();
- taskSolved += getSolvedTasks(lesson, taskManager);
+ taskSolved += getSolvedTasks(lesson);
}
String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum);
contentPanel.add(statisticLabel);
}
- private static int getSolvedTasks(@NotNull final Lesson lesson, StudyTaskManager taskManager) {
+ private static int getSolvedTasks(@NotNull final Lesson lesson) {
int solved = 0;
for (Task task : lesson.getTaskList()) {
- if (taskManager.getStatus(task) == StudyStatus.Solved) {
+ if (task.getStatus() == StudyStatus.Solved) {
solved += 1;
}
}
protected void onTaskFailed(String message) {
ApplicationManager.getApplication().invokeLater(() -> {
if (myTaskDir == null) return;
- myTaskManger.setStatus(myTask, StudyStatus.Failed);
+ myTask.setStatus(StudyStatus.Failed);
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
final String name = entry.getKey();
final TaskFile taskFile = entry.getValue();
if (taskFile.getAnswerPlaceholders().size() < 2) {
- myTaskManger.setStatus(taskFile, StudyStatus.Failed);
continue;
}
final Course course = myTaskManger.getCourse();