mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
EDU-619 Replace line-offset placeholder representation with absolute offset
This commit is contained in:
+70
-11
@@ -1,17 +1,32 @@
|
||||
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;
|
||||
|
||||
@@ -20,23 +35,67 @@ public class CCProjectComponent extends AbstractProjectComponent {
|
||||
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
|
||||
public String getComponentName() {
|
||||
return "CCProjectComponent";
|
||||
|
||||
+55
-7
@@ -19,14 +19,37 @@ 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.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;
|
||||
@@ -37,14 +60,39 @@ public class CCProjectService implements PersistentStateComponent<CCProjectServi
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ import java.io.IOException;
|
||||
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";
|
||||
|
||||
-3
@@ -1,6 +1,5 @@
|
||||
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;
|
||||
@@ -17,8 +16,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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();
|
||||
|
||||
+9
-9
@@ -30,10 +30,10 @@ public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction {
|
||||
}
|
||||
|
||||
|
||||
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)) {
|
||||
@@ -54,13 +54,12 @@ public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -103,7 +102,8 @@ public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction {
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction {
|
||||
}
|
||||
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) {
|
||||
|
||||
+3
-3
@@ -44,9 +44,9 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+13
-16
@@ -17,18 +17,17 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
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.*;
|
||||
@@ -62,19 +61,17 @@ public class CCFromCourseArchive extends DumbAwareAction {
|
||||
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);
|
||||
@@ -152,7 +149,7 @@ public class CCFromCourseArchive extends DumbAwareAction {
|
||||
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);
|
||||
|
||||
+2
-6
@@ -37,8 +37,7 @@ public class StudyAnswerPlaceholderExtendWordHandler implements ExtendWordSelect
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,10 +54,7 @@ public class StudyAnswerPlaceholderExtendWordHandler implements ExtendWordSelect
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
-135
@@ -1,24 +1,26 @@
|
||||
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;
|
||||
@@ -31,22 +33,30 @@ import java.util.Map;
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@@ -85,97 +95,8 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -186,7 +107,7 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
|
||||
}
|
||||
|
||||
public boolean hasFailedAnswerPlaceholders(@NotNull final TaskFile taskFile) {
|
||||
return taskFile.getAnswerPlaceholders().size() > 0 && getStatus(taskFile) == StudyStatus.Failed;
|
||||
return taskFile.getAnswerPlaceholders().size() > 0 && taskFile.hasFailedPlaceholders();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -194,7 +115,7 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
|
||||
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);
|
||||
}
|
||||
@@ -203,46 +124,34 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
|
||||
|
||||
@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();
|
||||
}
|
||||
int version = StudySerializationUtils.Xml.getVersion(state);
|
||||
if (version == -1) {
|
||||
LOG.error("StudyTaskManager doesn't contain any version:\n" + state.getValue());
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -279,13 +188,15 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
|
||||
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);
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.intellij.openapi.ui.popup.Balloon;
|
||||
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;
|
||||
@@ -680,4 +681,17 @@ public class StudyUtils {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ public class StudyFillPlaceholdersAction extends AnAction {
|
||||
if (answer == null) {
|
||||
continue;
|
||||
}
|
||||
int offset = placeholder.getRealStartOffset(document);
|
||||
int offset = placeholder.getOffset();
|
||||
document.deleteString(offset, offset + placeholder.getRealLength());
|
||||
document.insertString(offset, answer);
|
||||
}
|
||||
|
||||
+4
-4
@@ -50,11 +50,11 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction {
|
||||
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);
|
||||
@@ -96,6 +96,6 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction {
|
||||
}
|
||||
final Editor editor = studyState.getEditor();
|
||||
final TaskFile taskFile = studyState.getTaskFile();
|
||||
return taskFile.getAnswerPlaceholder(editor.getDocument(), editor.getCaretModel().getLogicalPosition());
|
||||
return taskFile.getAnswerPlaceholder(editor.getCaretModel().getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -5,7 +5,6 @@ import com.intellij.codeInsight.documentation.DocumentationManager;
|
||||
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;
|
||||
@@ -14,11 +13,11 @@ import com.intellij.openapi.util.Disposer;
|
||||
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;
|
||||
@@ -54,8 +53,9 @@ public class StudyShowHintAction extends StudyActionWithShortcut {
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -64,7 +64,6 @@ public class StudyShowHintAction extends StudyActionWithShortcut {
|
||||
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);
|
||||
|
||||
+3
-5
@@ -2,14 +2,13 @@ package com.jetbrains.edu.learning.actions;
|
||||
|
||||
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;
|
||||
@@ -38,7 +37,7 @@ abstract public class StudyWindowNavigationAction extends StudyActionWithShortcu
|
||||
if (nextAnswerPlaceholder == null) {
|
||||
return;
|
||||
}
|
||||
StudyNavigator.navigateToAnswerPlaceholder(selectedEditor, nextAnswerPlaceholder, selectedTaskFile);
|
||||
StudyNavigator.navigateToAnswerPlaceholder(selectedEditor, nextAnswerPlaceholder);
|
||||
selectedEditor.getSelectionModel().removeSelection();
|
||||
}
|
||||
}
|
||||
@@ -47,8 +46,7 @@ abstract public class StudyWindowNavigationAction extends StudyActionWithShortcu
|
||||
|
||||
@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
|
||||
|
||||
+4
-6
@@ -49,7 +49,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
myTask = studyState.getTask();
|
||||
myTaskDir = studyState.getTaskDir();
|
||||
myTaskManger = StudyTaskManager.getInstance(myProject);
|
||||
myStatusBeforeCheck = myTaskManger.getStatus(myTask);
|
||||
myStatusBeforeCheck = myTask.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,7 +67,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
myTaskManger.setStatus(myTask, myStatusBeforeCheck);
|
||||
myTask.setStatus(myStatusBeforeCheck);
|
||||
clearState();
|
||||
}
|
||||
|
||||
@@ -156,9 +156,8 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
}
|
||||
|
||||
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(
|
||||
@@ -175,9 +174,8 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ public class StudyCheckUtils {
|
||||
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);
|
||||
|
||||
+2
-2
@@ -54,10 +54,10 @@ public class StudySmartChecker {
|
||||
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);
|
||||
|
||||
+5
-2
@@ -36,7 +36,10 @@ public class EduAnswerPlaceholderPainter {
|
||||
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);
|
||||
@@ -66,7 +69,7 @@ public class EduAnswerPlaceholderPainter {
|
||||
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) {
|
||||
|
||||
+3
-6
@@ -39,10 +39,9 @@ public class EduDocumentListener extends DocumentAdapter {
|
||||
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));
|
||||
@@ -70,11 +69,9 @@ public class EduDocumentListener extends DocumentAdapter {
|
||||
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)));
|
||||
}
|
||||
|
||||
@@ -4,33 +4,24 @@ import com.intellij.ide.SaveAndSyncHandler;
|
||||
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;
|
||||
@@ -95,7 +86,7 @@ public class EduUtils {
|
||||
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);
|
||||
}
|
||||
@@ -186,8 +177,9 @@ public class EduUtils {
|
||||
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);
|
||||
@@ -200,7 +192,7 @@ public class EduUtils {
|
||||
@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);
|
||||
@@ -246,76 +238,6 @@ public class EduUtils {
|
||||
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) {
|
||||
|
||||
+48
-77
@@ -13,25 +13,34 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -57,22 +66,6 @@ public class AnswerPlaceholder {
|
||||
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;
|
||||
}
|
||||
@@ -93,7 +86,7 @@ public class AnswerPlaceholder {
|
||||
return myInitialState;
|
||||
}
|
||||
|
||||
public void setInitialState(@NotNull final MyInitialState initialState) {
|
||||
public void setInitialState(MyInitialState initialState) {
|
||||
myInitialState = initialState;
|
||||
}
|
||||
|
||||
@@ -115,10 +108,6 @@ public class AnswerPlaceholder {
|
||||
myTaskFile = taskFile;
|
||||
}
|
||||
|
||||
public int getRealStartOffset(@NotNull final Document document) {
|
||||
return document.getLineStartOffset(line) + start;
|
||||
}
|
||||
|
||||
public int getPossibleAnswerLength() {
|
||||
return possibleAnswer.length();
|
||||
}
|
||||
@@ -128,21 +117,16 @@ public class AnswerPlaceholder {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +148,7 @@ public class AnswerPlaceholder {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
setInitialState(new MyInitialState(line, myTaskText.length(), start));
|
||||
setInitialState(new MyInitialState(myOffset, myTaskText.length()));
|
||||
}
|
||||
|
||||
public boolean getUseLength() {
|
||||
@@ -182,53 +166,40 @@ public class AnswerPlaceholder {
|
||||
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;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AnswerPlaceholder that = (AnswerPlaceholder)o;
|
||||
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -5,12 +5,7 @@ import java.util.Comparator;
|
||||
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
|
||||
|
||||
+3
-1
@@ -4,7 +4,6 @@ import com.google.gson.annotations.Expose;
|
||||
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;
|
||||
@@ -29,6 +28,9 @@ public class Course {
|
||||
@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
|
||||
*/
|
||||
|
||||
+28
-5
@@ -12,12 +12,27 @@ import java.util.List;
|
||||
|
||||
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);
|
||||
@@ -74,4 +89,12 @@ public class Lesson implements StudyItem {
|
||||
return null;
|
||||
}
|
||||
|
||||
public StudyStatus getStatus() {
|
||||
for (Task task : taskList) {
|
||||
if (task.getStatus() != StudyStatus.Solved) {
|
||||
return StudyStatus.Unchecked;
|
||||
}
|
||||
}
|
||||
return StudyStatus.Solved;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
package com.jetbrains.edu.learning.courseFormat;
|
||||
|
||||
public enum StudyStatus {
|
||||
Unchecked, Solved, Failed, Uninitialized
|
||||
Unchecked, Solved, Failed
|
||||
}
|
||||
|
||||
+20
-6
@@ -20,13 +20,22 @@ import java.util.Map;
|
||||
* 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() {}
|
||||
@@ -205,5 +214,10 @@ public class Task implements StudyItem {
|
||||
|
||||
public void setStatus(StudyStatus status) {
|
||||
myStatus = status;
|
||||
for (TaskFile taskFile : taskFiles.values()) {
|
||||
for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
|
||||
placeholder.setStatus(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-55
@@ -2,8 +2,6 @@ package com.jetbrains.edu.learning.courseFormat;
|
||||
|
||||
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;
|
||||
@@ -27,6 +25,9 @@ public class TaskFile {
|
||||
@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();
|
||||
@@ -70,32 +71,16 @@ public class TaskFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
@@ -107,9 +92,8 @@ public class TaskFile {
|
||||
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());
|
||||
@@ -151,35 +135,12 @@ public class TaskFile {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -26,6 +26,7 @@ import com.intellij.psi.PsiFile;
|
||||
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;
|
||||
@@ -132,7 +133,7 @@ public class StudyProjectGenerator {
|
||||
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;
|
||||
|
||||
+2
-2
@@ -42,11 +42,11 @@ public class StudyEditorFactoryListener implements EditorFactoryListener {
|
||||
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);
|
||||
}
|
||||
|
||||
+6
-11
@@ -1,11 +1,9 @@
|
||||
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;
|
||||
|
||||
@@ -67,29 +65,26 @@ public class StudyNavigator {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
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();
|
||||
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
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;
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
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;
|
||||
}
|
||||
+4
-5
@@ -43,7 +43,7 @@ public class StudyDirectoryNode extends PsiDirectoryNode {
|
||||
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));
|
||||
@@ -89,9 +89,8 @@ public class StudyDirectoryNode extends PsiDirectoryNode {
|
||||
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;
|
||||
@@ -107,7 +106,7 @@ public class StudyDirectoryNode extends PsiDirectoryNode {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
+2
-2
@@ -81,8 +81,8 @@ public class CourseInfo {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ public class EduAdaptiveStepicConnector {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
+4
-4
@@ -1,8 +1,6 @@
|
||||
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;
|
||||
@@ -16,6 +14,7 @@ import com.intellij.openapi.vfs.VfsUtil;
|
||||
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;
|
||||
@@ -235,7 +234,7 @@ public class EduStepicConnector {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -718,6 +717,7 @@ public class EduStepicConnector {
|
||||
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));
|
||||
|
||||
+12
-12
@@ -10,16 +10,16 @@ import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -32,27 +32,27 @@ public class StepicUser {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -83,6 +83,6 @@ public class StepicUser {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return StringUtil.join(new String[]{firstName, lastName}, " ");
|
||||
return StringUtil.join(new String[]{myFirstName, myLastName}, " ");
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -43,7 +43,7 @@ public class StudyProgressToolWindowFactory implements ToolWindowFactory, DumbAw
|
||||
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);
|
||||
|
||||
@@ -68,10 +68,10 @@ public class StudyProgressToolWindowFactory implements ToolWindowFactory, DumbAw
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -95,12 +95,11 @@ public class PyStudyCheckAction extends StudyCheckAction {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user